From 96379678e6290428a7a0d840e8a62d8a9026beef Mon Sep 17 00:00:00 2001 From: Stiver Date: Thu, 13 Mar 2014 23:02:30 +0100 Subject: [PATCH] - method attribute 'default' (Java 8) - instruction 'invokedynamic' (Java 7) - bugfixing --- src/de/fernflower/code/CodeConstants.java | 11 + src/de/fernflower/code/ConstantsUtil.java | 19 +- src/de/fernflower/code/Instruction.java | 3 +- .../code/interpreter/InstructionImpact.java | 21 +- .../code/optinstructions/INVOKEDYNAMIC.java | 20 + .../main/ClassReference14Processor.java | 15 +- src/de/fernflower/main/ClassWriter.java | 5 + .../main/rels/MethodProcessorThread.java | 6 +- .../modules/decompiler/ExprProcessor.java | 13 +- .../modules/decompiler/FinallyProcessor.java | 57 ++- .../decompiler/exps/InvocationExprent.java | 22 +- .../sforms/FlattenStatementsHelper.java | 15 +- .../sforms/SSAConstructorSparseEx.java | 392 +++++++++--------- .../sforms/SSAUConstructorSparseEx.java | 83 ++-- src/de/fernflower/struct/StructClass.java | 23 + src/de/fernflower/struct/StructMethod.java | 15 +- 16 files changed, 419 insertions(+), 301 deletions(-) create mode 100644 src/de/fernflower/code/optinstructions/INVOKEDYNAMIC.java diff --git a/src/de/fernflower/code/CodeConstants.java b/src/de/fernflower/code/CodeConstants.java index 90ed425..111b3fd 100644 --- a/src/de/fernflower/code/CodeConstants.java +++ b/src/de/fernflower/code/CodeConstants.java @@ -16,6 +16,16 @@ package de.fernflower.code; public interface CodeConstants { + // ---------------------------------------------------------------------- + // BYTECODE VERSIONS + // ---------------------------------------------------------------------- + + public final static int BYTECODE_JAVA_LE_4 = 1; + public final static int BYTECODE_JAVA_5 = 2; + public final static int BYTECODE_JAVA_6 = 3; + public final static int BYTECODE_JAVA_7 = 4; + public final static int BYTECODE_JAVA_8 = 5; + // ---------------------------------------------------------------------- // VARIABLE TYPES // ---------------------------------------------------------------------- @@ -339,6 +349,7 @@ public interface CodeConstants { public final static int opc_invokespecial = 183; public final static int opc_invokestatic = 184; public final static int opc_invokeinterface = 185; + public final static int opc_invokedynamic = 186; public final static int opc_xxxunusedxxx = 186; public final static int opc_new = 187; public final static int opc_newarray = 188; diff --git a/src/de/fernflower/code/ConstantsUtil.java b/src/de/fernflower/code/ConstantsUtil.java index fda1c49..ed9c5bd 100644 --- a/src/de/fernflower/code/ConstantsUtil.java +++ b/src/de/fernflower/code/ConstantsUtil.java @@ -30,6 +30,7 @@ import de.fernflower.code.optinstructions.GOTO_W; import de.fernflower.code.optinstructions.IINC; import de.fernflower.code.optinstructions.ILOAD; import de.fernflower.code.optinstructions.INSTANCEOF; +import de.fernflower.code.optinstructions.INVOKEDYNAMIC; import de.fernflower.code.optinstructions.INVOKEINTERFACE; import de.fernflower.code.optinstructions.INVOKESPECIAL; import de.fernflower.code.optinstructions.INVOKESTATIC; @@ -58,17 +59,18 @@ public class ConstantsUtil { return opcodeNames[opcode]; } - public static Instruction getInstructionInstance(int opcode, boolean wide, int group, int[] operands) { + public static Instruction getInstructionInstance(int opcode, boolean wide, int group, int bytecode_version, int[] operands) { - Instruction instr = getInstructionInstance(opcode); + Instruction instr = getInstructionInstance(opcode, bytecode_version); instr.wide = wide; instr.group = group; + instr.bytecode_version = bytecode_version; instr.setOperands(operands); return instr; } - public static Instruction getInstructionInstance(int opcode) { + private static Instruction getInstructionInstance(int opcode, int bytecode_version) { try { Instruction instr; @@ -78,7 +80,13 @@ public class ConstantsUtil { opcode == CodeConstants.opc_ifnonnull) { instr = new IfInstruction(); } else { + Class cl = opcodeClasses[opcode]; + + if(opcode == CodeConstants.opc_invokedynamic && bytecode_version < CodeConstants.BYTECODE_JAVA_7) { + cl = null; // instruction unused in Java 6 and before + } + if(cl == null) { instr = new Instruction(); } else { @@ -282,7 +290,8 @@ public class ConstantsUtil { "invokespecial", // "invokespecial", "invokestatic", // "invokestatic", "invokeinterface", // "invokeinterface", - "xxxunusedxxx", // "xxxunusedxxx", + //"xxxunusedxxx", // "xxxunusedxxx", Java 6 and before + "invokedynamic", // "invokedynamic", Java 7 and later "new", // "new", "newarray", // "newarray", "anewarray", // "anewarray", @@ -487,7 +496,7 @@ public class ConstantsUtil { INVOKESPECIAL.class, // "invokespecial", INVOKESTATIC.class, // "invokestatic", INVOKEINTERFACE.class, // "invokeinterface", - null , // "xxxunusedxxx", + INVOKEDYNAMIC.class, // "xxxunusedxxx" Java 6 and before, "invokedynamic" Java 7 and later NEW.class, // "new", NEWARRAY.class, // "newarray", ANEWARRAY.class, // "anewarray", diff --git a/src/de/fernflower/code/Instruction.java b/src/de/fernflower/code/Instruction.java index ad76fc0..e74c4e9 100644 --- a/src/de/fernflower/code/Instruction.java +++ b/src/de/fernflower/code/Instruction.java @@ -29,6 +29,7 @@ public class Instruction implements CodeConstants { public boolean wide = false; + public int bytecode_version = BYTECODE_JAVA_LE_4; // ***************************************************************************** // private fields @@ -55,7 +56,7 @@ public class Instruction implements CodeConstants { } public Instruction clone() { - return ConstantsUtil.getInstructionInstance(opcode, wide, group, operands==null?null:(int[])operands.clone()); + return ConstantsUtil.getInstructionInstance(opcode, wide, group, bytecode_version, operands==null?null:(int[])operands.clone()); } public String toString() { diff --git a/src/de/fernflower/code/interpreter/InstructionImpact.java b/src/de/fernflower/code/interpreter/InstructionImpact.java index 7d7e9e3..588991e 100644 --- a/src/de/fernflower/code/interpreter/InstructionImpact.java +++ b/src/de/fernflower/code/interpreter/InstructionImpact.java @@ -413,15 +413,18 @@ public class InstructionImpact { case CodeConstants.opc_invokeinterface: stack.pop(); case CodeConstants.opc_invokestatic: - ck = pool.getLinkConstant(instr.getOperand(0)); - MethodDescriptor md = MethodDescriptor.parseDescriptor(ck.descriptor); - for(int i=0;i= CodeConstants.BYTECODE_JAVA_7) { + ck = pool.getLinkConstant(instr.getOperand(0)); + MethodDescriptor md = MethodDescriptor.parseDescriptor(ck.descriptor); + for(int i=0;i 48 || (major_version == 48 && minor_version > 0)) { +// int major_version = wrapper.getClassStruct().major_version; +// int minor_version = wrapper.getClassStruct().minor_version; +// +// if(major_version > 48 || (major_version == 48 && minor_version > 0)) { +// // version 1.5 or above +// return; +// } + + if(wrapper.getClassStruct().isVersionGE_1_5()) { // version 1.5 or above return; } - - // find the synthetic method Class class$(String) if present HashMap mapClassMeths = new HashMap(); diff --git a/src/de/fernflower/main/ClassWriter.java b/src/de/fernflower/main/ClassWriter.java index 66fbea1..4e49f20 100644 --- a/src/de/fernflower/main/ClassWriter.java +++ b/src/de/fernflower/main/ClassWriter.java @@ -586,6 +586,11 @@ public class ClassWriter { } } } + + // 'default' modifier (Java 8) + if(isInterface && mt.containsCode()) { + bufstrwriter.write("default "); + } GenericMethodDescriptor descriptor = null; if(DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { diff --git a/src/de/fernflower/main/rels/MethodProcessorThread.java b/src/de/fernflower/main/rels/MethodProcessorThread.java index ca789ed..a651e71 100644 --- a/src/de/fernflower/main/rels/MethodProcessorThread.java +++ b/src/de/fernflower/main/rels/MethodProcessorThread.java @@ -102,7 +102,7 @@ public class MethodProcessorThread implements Runnable { // System.out.println(); // } -// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern1.dot"), true); + //DotExporter.toDotFile(graph, new File("c:\\Temp\\fern1.dot"), true); DeadCodeHelper.removeDeadBlocks(graph); graph.inlineJsr(mt); @@ -142,7 +142,7 @@ public class MethodProcessorThread implements Runnable { DecompilerContext.getCountercontainer().setCounter(CounterContainer.VAR_COUNTER, mt.getLocalVariables()); //DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true); - System.out.println(graph.toString()); + //System.out.println(graph.toString()); if(ExceptionDeobfuscator.hasObfuscatedExceptions(graph)) { DecompilerContext.getLogger().writeMessage("Heavily obfuscated exception ranges found!", IFernflowerLogger.WARNING); @@ -157,7 +157,7 @@ public class MethodProcessorThread implements Runnable { //DotExporter.toDotFile(graph, new File("c:\\Temp\\fern2.dot"), true); //System.out.println(graph.toString()); - System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); + //System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); root = DomHelper.parseGraph(graph); } diff --git a/src/de/fernflower/modules/decompiler/ExprProcessor.java b/src/de/fernflower/modules/decompiler/ExprProcessor.java index a9a9878..c640bc1 100644 --- a/src/de/fernflower/modules/decompiler/ExprProcessor.java +++ b/src/de/fernflower/modules/decompiler/ExprProcessor.java @@ -527,11 +527,14 @@ public class ExprProcessor implements CodeConstants { case opc_invokespecial: case opc_invokestatic: case opc_invokeinterface: - InvocationExprent exprinv = new InvocationExprent(instr.opcode, pool.getLinkConstant(instr.getOperand(0)), stack); - if (exprinv.getDescriptor().ret.type == CodeConstants.TYPE_VOID) { - exprlist.add(exprinv); - } else { - pushEx(stack, exprlist, exprinv); + case opc_invokedynamic: + if(instr.opcode != opc_invokedynamic || instr.bytecode_version >= CodeConstants.BYTECODE_JAVA_7) { + InvocationExprent exprinv = new InvocationExprent(instr.opcode, pool.getLinkConstant(instr.getOperand(0)), stack); + if (exprinv.getDescriptor().ret.type == CodeConstants.TYPE_VOID) { + exprlist.add(exprinv); + } else { + pushEx(stack, exprlist, exprinv); + } } break; case opc_new: diff --git a/src/de/fernflower/modules/decompiler/FinallyProcessor.java b/src/de/fernflower/modules/decompiler/FinallyProcessor.java index 0d8d556..cf10f48 100644 --- a/src/de/fernflower/modules/decompiler/FinallyProcessor.java +++ b/src/de/fernflower/modules/decompiler/FinallyProcessor.java @@ -14,16 +14,13 @@ package de.fernflower.modules.decompiler; -import java.io.File; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map.Entry; -import test.util.DotExporter; import de.fernflower.code.CodeConstants; import de.fernflower.code.ConstantsUtil; import de.fernflower.code.Instruction; @@ -73,6 +70,8 @@ public class FinallyProcessor { private boolean processStatementEx(StructMethod mt, RootStatement root, ControlFlowGraph graph) { + int bytecode_version = mt.getClassStruct().getBytecodeVersion(); + LinkedList stack = new LinkedList(); stack.add(root); @@ -110,7 +109,7 @@ public class FinallyProcessor { } else { int varindex = DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER); - insertSemaphore(graph, getAllBasicBlocks(fin.getFirst()), head, handler, varindex, inf); + insertSemaphore(graph, getAllBasicBlocks(fin.getFirst()), head, handler, varindex, inf, bytecode_version); finallyBlockIDs.put(handler.id, varindex); } @@ -360,7 +359,7 @@ public class FinallyProcessor { return new Object[] {firstcode, mapLast}; } - private void insertSemaphore(ControlFlowGraph graph, HashSet setTry, BasicBlock head, BasicBlock handler, int var, Object[] information) { + private void insertSemaphore(ControlFlowGraph graph, HashSet setTry, BasicBlock head, BasicBlock handler, int var, Object[] information, int bytecode_version) { HashSet setCopy = new HashSet(setTry); @@ -389,8 +388,8 @@ public class FinallyProcessor { // disable semaphore SimpleInstructionSequence seq = new SimpleInstructionSequence(); - seq.addInstruction(ConstantsUtil.getInstructionInstance(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, new int[]{0}) , -1); - seq.addInstruction(ConstantsUtil.getInstructionInstance(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, new int[]{var}) , -1); + seq.addInstruction(ConstantsUtil.getInstructionInstance(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}) , -1); + seq.addInstruction(ConstantsUtil.getInstructionInstance(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}) , -1); // build a separate block BasicBlock newblock = new BasicBlock(++graph.last_id); @@ -419,8 +418,8 @@ public class FinallyProcessor { // enable semaphor at the statement entrance SimpleInstructionSequence seq = new SimpleInstructionSequence(); - seq.addInstruction(ConstantsUtil.getInstructionInstance(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, new int[]{1}) , -1); - seq.addInstruction(ConstantsUtil.getInstructionInstance(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, new int[]{var}) , -1); + seq.addInstruction(ConstantsUtil.getInstructionInstance(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{1}) , -1); + seq.addInstruction(ConstantsUtil.getInstructionInstance(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}) , -1); BasicBlock newhead = new BasicBlock(++graph.last_id); newhead.setSeq(seq); @@ -429,8 +428,8 @@ public class FinallyProcessor { // initialize semaphor with false seq = new SimpleInstructionSequence(); - seq.addInstruction(ConstantsUtil.getInstructionInstance(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, new int[]{0}) , -1); - seq.addInstruction(ConstantsUtil.getInstructionInstance(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, new int[]{var}) , -1); + seq.addInstruction(ConstantsUtil.getInstructionInstance(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}) , -1); + seq.addInstruction(ConstantsUtil.getInstructionInstance(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}) , -1); BasicBlock newheadinit = new BasicBlock(++graph.last_id); newheadinit.setSeq(seq); @@ -566,9 +565,9 @@ public class FinallyProcessor { lstAreas.add(new Object[] {start, arr[0], arr[1]}); } - try { - DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true); - } catch(Exception ex){ex.printStackTrace();} +// try { +// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true); +// } catch(Exception ex){ex.printStackTrace();} // delete areas for(Object[] area: lstAreas) { @@ -941,25 +940,25 @@ public class FinallyProcessor { // remove all the blocks inbetween for(BasicBlock block: setBlocks) { - if(!block.getSuccExceptions().containsAll(setCommonExceptionHandlers)) { - is_outside_range = true; - } - - HashSet setRemovedExceptionRanges = new HashSet(); - for(BasicBlock handler : block.getSuccExceptions()) { - setRemovedExceptionRanges.add(graph.getExceptionRange(handler, block)); - } - - if(setCommonRemovedExceptionRanges == null) { - setCommonRemovedExceptionRanges = setRemovedExceptionRanges; - } else { - setCommonRemovedExceptionRanges.retainAll(setRemovedExceptionRanges); - } - // artificial basic blocks (those resulted from splitting) // can belong to more than one area if(graph.getBlocks().containsKey(block.id)) { + if(!block.getSuccExceptions().containsAll(setCommonExceptionHandlers)) { + is_outside_range = true; + } + + HashSet setRemovedExceptionRanges = new HashSet(); + for(BasicBlock handler : block.getSuccExceptions()) { + setRemovedExceptionRanges.add(graph.getExceptionRange(handler, block)); + } + + if(setCommonRemovedExceptionRanges == null) { + setCommonRemovedExceptionRanges = setRemovedExceptionRanges; + } else { + setCommonRemovedExceptionRanges.retainAll(setRemovedExceptionRanges); + } + // shift extern edges on splitted blocks if(block.getSeq().isEmpty() && block.getSuccs().size() == 1) { BasicBlock succs = block.getSuccs().get(0); diff --git a/src/de/fernflower/modules/decompiler/exps/InvocationExprent.java b/src/de/fernflower/modules/decompiler/exps/InvocationExprent.java index ee0a798..5f0f5c1 100644 --- a/src/de/fernflower/modules/decompiler/exps/InvocationExprent.java +++ b/src/de/fernflower/modules/decompiler/exps/InvocationExprent.java @@ -43,6 +43,7 @@ public class InvocationExprent extends Exprent { public static final int INVOKE_VIRTUAL = 2; public static final int INVOKE_STATIC = 3; public static final int INVOKE_INTERFACE = 4; + public static final int INVOKE_DYNAMIC = 5; public static final int TYP_GENERAL = 1; public static final int TYP_INIT = 2; @@ -93,6 +94,10 @@ public class InvocationExprent extends Exprent { break; case CodeConstants.opc_invokeinterface: invocationTyp = INVOKE_INTERFACE; + break; + case CodeConstants.opc_invokedynamic: + invocationTyp = INVOKE_DYNAMIC; + classname = "java/lang/Class"; // dummy class name } if("".equals(name)) { @@ -108,7 +113,7 @@ public class InvocationExprent extends Exprent { lstParameters.add(0, stack.pop()); } - if(opcode == CodeConstants.opc_invokestatic) { + if(opcode == CodeConstants.opc_invokestatic || opcode == CodeConstants.opc_invokedynamic) { isStatic = true; } else { instance = stack.pop(); @@ -174,9 +179,11 @@ public class InvocationExprent extends Exprent { boolean isInstanceThis = false; if(isStatic) { - ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE); - if(node == null || !classname.equals(node.classStruct.qualifiedName)) { - buf.append(DecompilerContext.getImpcollector().getShortName(ExprProcessor.buildJavaClassName(classname))); + if(invocationTyp != INVOKE_DYNAMIC) { + ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE); + if(node == null || !classname.equals(node.classStruct.qualifiedName)) { + buf.append(DecompilerContext.getImpcollector().getShortName(ExprProcessor.buildJavaClassName(classname))); + } } } else { @@ -249,7 +256,12 @@ public class InvocationExprent extends Exprent { buf.append("."); } - buf.append(name+"("); + buf.append(name); + if(invocationTyp == INVOKE_DYNAMIC) { + buf.append(""); + } + buf.append("("); + break; case TYP_CLINIT: throw new RuntimeException("Explicite invocation of "); diff --git a/src/de/fernflower/modules/decompiler/sforms/FlattenStatementsHelper.java b/src/de/fernflower/modules/decompiler/sforms/FlattenStatementsHelper.java index 8fd1fce..b73a45e 100644 --- a/src/de/fernflower/modules/decompiler/sforms/FlattenStatementsHelper.java +++ b/src/de/fernflower/modules/decompiler/sforms/FlattenStatementsHelper.java @@ -408,17 +408,19 @@ public class FlattenStatementsHelper { if(finallyShortRangeSource != null) { + boolean isContinueEdge = (edgetype == StatEdge.TYPE_CONTINUE); + List lst = mapShortRangeFinallyPathIds.get(sourcenode.id); if(lst == null) { mapShortRangeFinallyPathIds.put(sourcenode.id, lst = new ArrayList()); } - lst.add(new String[]{finallyShortRangeSource.id, destination.id.toString(), finallyShortRangeEntry.id.toString(), isFinallyMonitorExceptionPath?"1":null}); + lst.add(new String[]{finallyShortRangeSource.id, destination.id.toString(), finallyShortRangeEntry.id.toString(), isFinallyMonitorExceptionPath?"1":null, isContinueEdge?"1":null}); lst = mapLongRangeFinallyPathIds.get(sourcenode.id); if(lst == null) { mapLongRangeFinallyPathIds.put(sourcenode.id, lst = new ArrayList()); } - lst.add(new String[]{finallyLongRangeSource.id, destination.id.toString(), finallyLongRangeEntry.id.toString()}); + lst.add(new String[]{finallyLongRangeSource.id, destination.id.toString(), finallyLongRangeEntry.id.toString(), isContinueEdge?"1":null}); } } @@ -454,7 +456,10 @@ public class FlattenStatementsHelper { List lst = ent.getValue(); for(String[] arr : lst) { - DirectNode dest = graph.nodes.getWithKey(mapDestinationNodes.get(Integer.parseInt(arr[1]))[0]); + + boolean isContinueEdge = arr[i==0?4:3] != null; + + DirectNode dest = graph.nodes.getWithKey(mapDestinationNodes.get(Integer.parseInt(arr[1]))[isContinueEdge?1:0]); DirectNode enter = graph.nodes.getWithKey(mapDestinationNodes.get(Integer.parseInt(arr[2]))[0]); newLst.add(new FinallyPathWrapper(arr[0], dest.id, enter.id)); @@ -505,6 +510,10 @@ public class FlattenStatementsHelper { public int hashCode() { return (source+":"+destination+":"+entry).hashCode(); } + + public String toString() { + return source + "->(" + entry + ")->" + destination; + } } diff --git a/src/de/fernflower/modules/decompiler/sforms/SSAConstructorSparseEx.java b/src/de/fernflower/modules/decompiler/sforms/SSAConstructorSparseEx.java index 8db2fa7..528129c 100644 --- a/src/de/fernflower/modules/decompiler/sforms/SSAConstructorSparseEx.java +++ b/src/de/fernflower/modules/decompiler/sforms/SSAConstructorSparseEx.java @@ -14,12 +14,14 @@ package de.fernflower.modules.decompiler.sforms; +import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map.Entry; +import test.util.DotExporter; import de.fernflower.code.CodeConstants; import de.fernflower.modules.decompiler.exps.AssignmentExprent; import de.fernflower.modules.decompiler.exps.Exprent; @@ -34,134 +36,143 @@ import de.fernflower.modules.decompiler.vars.VarVersionPaar; import de.fernflower.struct.StructMethod; import de.fernflower.struct.gen.MethodDescriptor; import de.fernflower.util.FastSparseSetFactory; +import de.fernflower.util.FastSparseSetFactory.FastSparseSet; import de.fernflower.util.InterpreterUtil; import de.fernflower.util.SFormsFastMapDirect; -import de.fernflower.util.FastSparseSetFactory.FastSparseSet; public class SSAConstructorSparseEx { // node id, var, version private HashMap inVarVersions = new HashMap(); - + // node id, var, version (direct branch) private HashMap outVarVersions = new HashMap(); // node id, var, version (negative branch) private HashMap outNegVarVersions = new HashMap(); - + // node id, var, version private HashMap extraVarVersions = new HashMap(); - + // (var, version), version - private HashMap> phi = new HashMap>(); - + private HashMap> phi = new HashMap>(); + // var, version - private HashMap lastversion = new HashMap(); - + private HashMap lastversion = new HashMap(); + private List startVars = new ArrayList(); // set factory private FastSparseSetFactory factory; - + public void splitVariables(RootStatement root, StructMethod mt) { - + FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); DirectGraph dgraph = flatthelper.buildDirectGraph(root); - -// try { -// DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr12_my.dot")); -// } catch(Exception ex) {ex.printStackTrace();} - + + // try { + // DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr12_my.dot")); + // } catch(Exception ex) {ex.printStackTrace();} + HashSet setInit = new HashSet(); - for(int i=0;i<64;i++) { + for(int i = 0; i < 64; i++) { setInit.add(i); } factory = new FastSparseSetFactory(setInit); - - SFormsFastMapDirect firstmap = createFirstMap(mt); + + SFormsFastMapDirect firstmap = createFirstMap(mt); extraVarVersions.put(dgraph.first.id, firstmap); - + setCatchMaps(root, dgraph, flatthelper); - + HashSet updated = new HashSet(); do { -// System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava()); + // System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava()); ssaStatements(dgraph, updated); -// System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava()); - } while(!updated.isEmpty()); + // System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava()); + } while (!updated.isEmpty()); } - + private void ssaStatements(DirectGraph dgraph, HashSet updated) { - -// try { -// DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr1_my.dot")); -// } catch(Exception ex) {ex.printStackTrace();} - - for(DirectNode node: dgraph.nodes) { - + + // try { + // DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr1_my.dot")); + // } catch(Exception ex) {ex.printStackTrace();} + + for(DirectNode node : dgraph.nodes) { + +// if (node.id.endsWith("_inc")) { +// System.out.println(); +// +// try { +// DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr1_my.dot")); +// } catch (Exception ex) { +// ex.printStackTrace(); +// } +// } + updated.remove(node.id); mergeInVarMaps(node, dgraph); - + SFormsFastMapDirect varmap = inVarVersions.get(node.id); varmap = new SFormsFastMapDirect(varmap); - - SFormsFastMapDirect[] varmaparr = new SFormsFastMapDirect[] {varmap, null}; - - if(node.exprents != null) { - for(Exprent expr: node.exprents) { + + SFormsFastMapDirect[] varmaparr = new SFormsFastMapDirect[] { varmap, null }; + + if (node.exprents != null) { + for(Exprent expr : node.exprents) { processExprent(expr, varmaparr); } } - - if(varmaparr[1] == null) { + + if (varmaparr[1] == null) { varmaparr[1] = varmaparr[0]; } - + boolean this_updated = !mapsEqual(varmaparr[0], outVarVersions.get(node.id)) - || (outNegVarVersions.containsKey(node.id) && !mapsEqual(varmaparr[1], outNegVarVersions.get(node.id))); - - if(this_updated) { + || (outNegVarVersions.containsKey(node.id) && !mapsEqual(varmaparr[1], outNegVarVersions.get(node.id))); + + if (this_updated) { outVarVersions.put(node.id, varmaparr[0]); - if(dgraph.mapNegIfBranch.containsKey(node.id)) { + if (dgraph.mapNegIfBranch.containsKey(node.id)) { outNegVarVersions.put(node.id, varmaparr[1]); } - - for(DirectNode nd: node.succs) { + + for(DirectNode nd : node.succs) { updated.add(nd.id); } } } - + } - + private void processExprent(Exprent expr, SFormsFastMapDirect[] varmaparr) { - - if(expr == null) { + + if (expr == null) { return; } - - - VarExprent varassign = null; + + VarExprent varassign = null; boolean finished = false; - - switch(expr.type) { + + switch (expr.type) { case Exprent.EXPRENT_ASSIGNMENT: - AssignmentExprent assexpr = (AssignmentExprent)expr; - if(assexpr.getCondtype() == AssignmentExprent.CONDITION_NONE) { + AssignmentExprent assexpr = (AssignmentExprent) expr; + if (assexpr.getCondtype() == AssignmentExprent.CONDITION_NONE) { Exprent dest = assexpr.getLeft(); - if(dest.type == Exprent.EXPRENT_VAR) { - varassign = (VarExprent)dest; + if (dest.type == Exprent.EXPRENT_VAR) { + varassign = (VarExprent) dest; } } break; case Exprent.EXPRENT_FUNCTION: - FunctionExprent func = (FunctionExprent)expr; - switch(func.getFunctype()) { + FunctionExprent func = (FunctionExprent) expr; + switch (func.getFunctype()) { case FunctionExprent.FUNCTION_IIF: processExprent(func.getLstOperands().get(0), varmaparr); SFormsFastMapDirect varmapFalse; - if(varmaparr[1] == null) { + if (varmaparr[1] == null) { varmapFalse = new SFormsFastMapDirect(varmaparr[0]); } else { varmapFalse = varmaparr[1]; @@ -169,92 +180,90 @@ public class SSAConstructorSparseEx { } processExprent(func.getLstOperands().get(1), varmaparr); - - SFormsFastMapDirect[] varmaparrNeg = new SFormsFastMapDirect[] {varmapFalse, null}; + + SFormsFastMapDirect[] varmaparrNeg = new SFormsFastMapDirect[] { varmapFalse, null }; processExprent(func.getLstOperands().get(2), varmaparrNeg); - + mergeMaps(varmaparr[0], varmaparrNeg[0]); varmaparr[1] = null; - - finished = true; + + finished = true; break; case FunctionExprent.FUNCTION_CADD: processExprent(func.getLstOperands().get(0), varmaparr); - SFormsFastMapDirect[] varmaparrAnd = new SFormsFastMapDirect[] {new SFormsFastMapDirect(varmaparr[0]), null}; - + SFormsFastMapDirect[] varmaparrAnd = new SFormsFastMapDirect[] { new SFormsFastMapDirect(varmaparr[0]), null }; + processExprent(func.getLstOperands().get(1), varmaparrAnd); - + // false map - varmaparr[1] = mergeMaps(varmaparr[varmaparr[1]==null?0:1], varmaparrAnd[varmaparrAnd[1]==null?0:1]); + varmaparr[1] = mergeMaps(varmaparr[varmaparr[1] == null ? 0 : 1], varmaparrAnd[varmaparrAnd[1] == null ? 0 : 1]); // true map varmaparr[0] = varmaparrAnd[0]; - + finished = true; break; case FunctionExprent.FUNCTION_COR: processExprent(func.getLstOperands().get(0), varmaparr); - SFormsFastMapDirect[] varmaparrOr = new SFormsFastMapDirect[] {new SFormsFastMapDirect(varmaparr[varmaparr[1]==null?0:1]), null}; - + SFormsFastMapDirect[] varmaparrOr = new SFormsFastMapDirect[] { new SFormsFastMapDirect(varmaparr[varmaparr[1] == null ? 0 : 1]), null }; + processExprent(func.getLstOperands().get(1), varmaparrOr); // false map - varmaparr[1] = varmaparrOr[varmaparrOr[1]==null?0:1]; + varmaparr[1] = varmaparrOr[varmaparrOr[1] == null ? 0 : 1]; // true map varmaparr[0] = mergeMaps(varmaparr[0], varmaparrOr[0]); finished = true; } } - - if(finished) { + + if (finished) { return; } - List lst = expr.getAllExprents(); lst.remove(varassign); - for(Exprent ex: lst) { + for(Exprent ex : lst) { processExprent(ex, varmaparr); } - - + SFormsFastMapDirect varmap = varmaparr[0]; - - if(varassign != null) { - + + if (varassign != null) { + Integer varindex = varassign.getIndex(); - if(varassign.getVersion() == 0) { + if (varassign.getVersion() == 0) { // get next version Integer nextver = getNextFreeVersion(varindex); - + // set version varassign.setVersion(nextver); - + setCurrentVar(varmap, varindex, nextver); } else { setCurrentVar(varmap, varindex, varassign.getVersion()); } - - } else if(expr.type == Exprent.EXPRENT_VAR) { - - VarExprent vardest = (VarExprent)expr; + + } else if (expr.type == Exprent.EXPRENT_VAR) { + + VarExprent vardest = (VarExprent) expr; Integer varindex = vardest.getIndex(); FastSparseSet vers = varmap.get(varindex); - + int cardinality = vers.getCardinality(); - if(cardinality == 1) { // == 1 + if (cardinality == 1) { // == 1 // set version Integer it = vers.iterator().next(); vardest.setVersion(it.intValue()); - } else if(cardinality == 2) { // size > 1 - Integer current_vers = vardest.getVersion(); - + } else if (cardinality == 2) { // size > 1 + Integer current_vers = vardest.getVersion(); + VarVersionPaar currpaar = new VarVersionPaar(varindex, current_vers); - if(current_vers != 0 && phi.containsKey(currpaar)) { + if (current_vers != 0 && phi.containsKey(currpaar)) { setCurrentVar(varmap, varindex, current_vers); // update phi node phi.get(currpaar).union(vers); @@ -263,111 +272,110 @@ public class SSAConstructorSparseEx { Integer nextver = getNextFreeVersion(varindex); // set version vardest.setVersion(nextver); - + setCurrentVar(varmap, varindex, nextver); // create new phi node phi.put(new VarVersionPaar(varindex, nextver), vers); - + } - } // 0 means uninitialized variable, which is impossible + } // 0 means uninitialized variable, which is impossible } } - + private Integer getNextFreeVersion(Integer var) { Integer nextver = lastversion.get(var); - if(nextver==null) { + if (nextver == null) { nextver = new Integer(1); } else { - nextver = new Integer(nextver.intValue()+1); + nextver = new Integer(nextver.intValue() + 1); } lastversion.put(var, nextver); return nextver; } - + private void mergeInVarMaps(DirectNode node, DirectGraph dgraph) { - + SFormsFastMapDirect mapNew = new SFormsFastMapDirect(); - - for(DirectNode pred: node.preds) { + + for(DirectNode pred : node.preds) { SFormsFastMapDirect mapOut = getFilteredOutMap(node.id, pred.id, dgraph, node.id); - if(mapNew.isEmpty()) { + if (mapNew.isEmpty()) { mapNew = mapOut.getCopy(); } else { mergeMaps(mapNew, mapOut); } } - - if(extraVarVersions.containsKey(node.id)) { + + if (extraVarVersions.containsKey(node.id)) { SFormsFastMapDirect mapExtra = extraVarVersions.get(node.id); - if(mapNew.isEmpty()) { + if (mapNew.isEmpty()) { mapNew = mapExtra.getCopy(); } else { mergeMaps(mapNew, mapExtra); } } - + inVarVersions.put(node.id, mapNew); } - - private SFormsFastMapDirect getFilteredOutMap(String nodeid, String predid, DirectGraph dgraph, - String destid) { - + + private SFormsFastMapDirect getFilteredOutMap(String nodeid, String predid, DirectGraph dgraph, String destid) { + SFormsFastMapDirect mapNew = new SFormsFastMapDirect(); - if(nodeid.equals(dgraph.mapNegIfBranch.get(predid))) { - if(outNegVarVersions.containsKey(predid)) { + if (nodeid.equals(dgraph.mapNegIfBranch.get(predid))) { + if (outNegVarVersions.containsKey(predid)) { mapNew = outNegVarVersions.get(predid).getCopy(); } - } else if(outVarVersions.containsKey(predid)) { + } else if (outVarVersions.containsKey(predid)) { mapNew = outVarVersions.get(predid).getCopy(); } - + boolean isFinallyExit = dgraph.mapShortRangeFinallyPaths.containsKey(predid); - - if(isFinallyExit && !mapNew.isEmpty()) { - + + if (isFinallyExit && !mapNew.isEmpty()) { + SFormsFastMapDirect mapNewTemp = mapNew.getCopy(); - + SFormsFastMapDirect mapTrueSource = new SFormsFastMapDirect(); - + String exceptionDest = dgraph.mapFinallyMonitorExceptionPathExits.get(predid); boolean isExceptionMonitorExit = (exceptionDest != null && !nodeid.equals(exceptionDest)); - + HashSet setLongPathWrapper = new HashSet(); for(FinallyPathWrapper finwraplong : dgraph.mapLongRangeFinallyPaths.get(predid)) { - setLongPathWrapper.add(finwraplong.destination+"##"+finwraplong.source); + setLongPathWrapper.add(finwraplong.destination + "##" + finwraplong.source); } - + for(FinallyPathWrapper finwrap : dgraph.mapShortRangeFinallyPaths.get(predid)) { SFormsFastMapDirect map; - + boolean recFinally = dgraph.mapShortRangeFinallyPaths.containsKey(finwrap.source); - - if(recFinally) { + + if (recFinally) { // recursion - map = getFilteredOutMap(finwrap.entry, finwrap.source, dgraph, destid); + map = getFilteredOutMap(finwrap.entry, finwrap.source, dgraph, destid); } else { - if(finwrap.entry.equals(dgraph.mapNegIfBranch.get(finwrap.source))) { + if (finwrap.entry.equals(dgraph.mapNegIfBranch.get(finwrap.source))) { map = outNegVarVersions.get(finwrap.source); } else { map = outVarVersions.get(finwrap.source); } } - + // false path? boolean isFalsePath = true; - - if(recFinally) { + + if (recFinally) { isFalsePath = !finwrap.destination.equals(nodeid); } else { - isFalsePath = !setLongPathWrapper.contains(destid+"##"+finwrap.source); + isFalsePath = !setLongPathWrapper.contains(destid + "##" + finwrap.source); } - - if(isFalsePath) { + + if (isFalsePath) { mapNewTemp.complement(map); } else { - if(mapTrueSource.isEmpty()) { - if(map != null) { + if (mapTrueSource.isEmpty()) { + if (map != null) { mapTrueSource = map.getCopy(); } } else { @@ -376,8 +384,8 @@ public class SSAConstructorSparseEx { } } - if(isExceptionMonitorExit) { - + if (isExceptionMonitorExit) { + mapNew = mapTrueSource; } else { @@ -385,119 +393,117 @@ public class SSAConstructorSparseEx { mapNewTemp.union(mapTrueSource); SFormsFastMapDirect oldInMap = inVarVersions.get(nodeid); - if(oldInMap != null) { + if (oldInMap != null) { mapNewTemp.union(oldInMap); } mapNew.intersection(mapNewTemp); } } - - return mapNew; + + return mapNew; } - - + private SFormsFastMapDirect mergeMaps(SFormsFastMapDirect mapTo, SFormsFastMapDirect map2) { - - if(map2 != null && !map2.isEmpty()) { + + if (map2 != null && !map2.isEmpty()) { mapTo.union(map2); } - + return mapTo; } - + private boolean mapsEqual(SFormsFastMapDirect map1, SFormsFastMapDirect map2) { - - if(map1 == null) { + + if (map1 == null) { return map2 == null; } else if (map2 == null) { return false; } - - if(map1.size() != map2.size()) { + + if (map1.size() != map2.size()) { return false; } - - for(Entry> ent2: map2.entryList()) { - if(!InterpreterUtil.equalObjects(map1.get(ent2.getKey()), ent2.getValue())) { + + for(Entry> ent2 : map2.entryList()) { + if (!InterpreterUtil.equalObjects(map1.get(ent2.getKey()), ent2.getValue())) { return false; } } - + return true; } - - + private void setCurrentVar(SFormsFastMapDirect varmap, Integer var, Integer vers) { FastSparseSet set = factory.spawnEmptySet(); set.add(vers); varmap.put(var, set); } - + private void setCatchMaps(Statement stat, DirectGraph dgraph, FlattenStatementsHelper flatthelper) { - + SFormsFastMapDirect map; - - switch(stat.type) { + + switch (stat.type) { case Statement.TYPE_CATCHALL: case Statement.TYPE_TRYCATCH: - List lstVars; - if(stat.type == Statement.TYPE_CATCHALL) { - lstVars = ((CatchAllStatement)stat).getVars(); + List lstVars; + if (stat.type == Statement.TYPE_CATCHALL) { + lstVars = ((CatchAllStatement) stat).getVars(); } else { - lstVars = ((CatchStatement)stat).getVars(); + lstVars = ((CatchStatement) stat).getVars(); } - - for(int i=1;i set = factory.spawnEmptySet(); set.add(version); map.put(varindex, set); startVars.add(new VarVersionPaar(varindex, version)); - - if(thisvar) { - if(i==0) { + + if (thisvar) { + if (i == 0) { varindex++; } else { - varindex+=md.params[i-1].stack_size; + varindex += md.params[i - 1].stack_size; } } else { - varindex+=md.params[i].stack_size; + varindex += md.params[i].stack_size; } } - + return map; } - + public HashMap> getPhi() { return phi; } @@ -505,5 +511,5 @@ public class SSAConstructorSparseEx { public List getStartVars() { return startVars; } - + } diff --git a/src/de/fernflower/modules/decompiler/sforms/SSAUConstructorSparseEx.java b/src/de/fernflower/modules/decompiler/sforms/SSAUConstructorSparseEx.java index ec232fa..ef51e90 100644 --- a/src/de/fernflower/modules/decompiler/sforms/SSAUConstructorSparseEx.java +++ b/src/de/fernflower/modules/decompiler/sforms/SSAUConstructorSparseEx.java @@ -110,9 +110,9 @@ public class SSAUConstructorSparseEx { HashSet updated = new HashSet(); do { -// System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava()); + //System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava()); ssaStatements(dgraph, updated, false); -// System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava()); + //System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava()); } while(!updated.isEmpty()); @@ -146,6 +146,7 @@ public class SSAUConstructorSparseEx { || (outNegVarVersions.containsKey(node.id) && !mapsEqual(varmaparr[1], outNegVarVersions.get(node.id))); if(this_updated) { + outVarVersions.put(node.id, varmaparr[0]); if(dgraph.mapNegIfBranch.containsKey(node.id)) { outNegVarVersions.put(node.id, varmaparr[1]); @@ -246,45 +247,45 @@ public class SSAUConstructorSparseEx { SFormsFastMapDirect varmap = varmaparr[0]; - // field access - if(expr.type == Exprent.EXPRENT_FIELD) { - - int index; - if(mapFieldVars.containsKey(expr.id)) { - index = mapFieldVars.get(expr.id); - } else { - index = fieldvarcounter--; - mapFieldVars.put(expr.id, index); - - // ssu graph - ssuversions.createNode(new VarVersionPaar(index, 1)); - } - - setCurrentVar(varmap, index, 1); - - } else if(expr.type == Exprent.EXPRENT_INVOCATION || - (expr.type == Exprent.EXPRENT_ASSIGNMENT && ((AssignmentExprent)expr).getLeft().type == Exprent.EXPRENT_FIELD) || - (expr.type == Exprent.EXPRENT_NEW && ((NewExprent)expr).getNewtype().type == CodeConstants.TYPE_OBJECT) || - expr.type == Exprent.EXPRENT_FUNCTION) { - - boolean ismmpp = true; - - if(expr.type == Exprent.EXPRENT_FUNCTION) { - - ismmpp = false; - - FunctionExprent fexpr = (FunctionExprent)expr; - if(fexpr.getFunctype() >= FunctionExprent.FUNCTION_IMM && fexpr.getFunctype() <= FunctionExprent.FUNCTION_PPI) { - if(fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) { - ismmpp = true; - } - } - } - - if(ismmpp) { - varmap.removeAllFields(); - } - } +// // field access +// if(expr.type == Exprent.EXPRENT_FIELD) { +// +// int index; +// if(mapFieldVars.containsKey(expr.id)) { +// index = mapFieldVars.get(expr.id); +// } else { +// index = fieldvarcounter--; +// mapFieldVars.put(expr.id, index); +// +// // ssu graph +// ssuversions.createNode(new VarVersionPaar(index, 1)); +// } +// +// setCurrentVar(varmap, index, 1); +// +// } else if(expr.type == Exprent.EXPRENT_INVOCATION || +// (expr.type == Exprent.EXPRENT_ASSIGNMENT && ((AssignmentExprent)expr).getLeft().type == Exprent.EXPRENT_FIELD) || +// (expr.type == Exprent.EXPRENT_NEW && ((NewExprent)expr).getNewtype().type == CodeConstants.TYPE_OBJECT) || +// expr.type == Exprent.EXPRENT_FUNCTION) { +// +// boolean ismmpp = true; +// +// if(expr.type == Exprent.EXPRENT_FUNCTION) { +// +// ismmpp = false; +// +// FunctionExprent fexpr = (FunctionExprent)expr; +// if(fexpr.getFunctype() >= FunctionExprent.FUNCTION_IMM && fexpr.getFunctype() <= FunctionExprent.FUNCTION_PPI) { +// if(fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) { +// ismmpp = true; +// } +// } +// } +// +// if(ismmpp) { +// varmap.removeAllFields(); +// } +// } if(varassign != null) { diff --git a/src/de/fernflower/struct/StructClass.java b/src/de/fernflower/struct/StructClass.java index 75a55b1..de2e856 100644 --- a/src/de/fernflower/struct/StructClass.java +++ b/src/de/fernflower/struct/StructClass.java @@ -22,6 +22,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import de.fernflower.code.CodeConstants; import de.fernflower.struct.attr.StructGeneralAttribute; import de.fernflower.struct.consts.ConstantPool; import de.fernflower.struct.consts.PrimitiveConstant; @@ -318,4 +319,26 @@ public class StructClass { return loader; } + public boolean isVersionGE_1_5() { + return (major_version > 48 || (major_version == 48 && minor_version > 0)); // FIXME: check second condition + } + + public boolean isVersionGE_1_7() { + return (major_version >= 51); + } + + public int getBytecodeVersion() { + switch(major_version) { + case 52: + return CodeConstants.BYTECODE_JAVA_8; + case 51: + return CodeConstants.BYTECODE_JAVA_7; + case 50: + return CodeConstants.BYTECODE_JAVA_6; + case 49: + return CodeConstants.BYTECODE_JAVA_5; + } + + return CodeConstants.BYTECODE_JAVA_LE_4; + } } diff --git a/src/de/fernflower/struct/StructMethod.java b/src/de/fernflower/struct/StructMethod.java index 3cc80c9..035fa68 100644 --- a/src/de/fernflower/struct/StructMethod.java +++ b/src/de/fernflower/struct/StructMethod.java @@ -280,6 +280,8 @@ public class StructMethod implements CodeConstants { VBStyleCollection collinstr = new VBStyleCollection(); + int bytecode_version = classStruct.getBytecodeVersion(); + for(int i=0;i