From 7f9bb7d21c105347ab987df050eb7458ac8ab1f5 Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Thu, 6 Aug 2015 17:01:25 -0700 Subject: [PATCH 01/73] Aded debug printer to MethodProcessorRunnable for the method outline. --- .../main/rels/MethodProcessorRunnable.java | 123 +++++++++++++++++- 1 file changed, 122 insertions(+), 1 deletion(-) diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java index 1c90fb6..dc6f2f0 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java @@ -17,6 +17,7 @@ import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.InstructionSequence; +import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; @@ -25,12 +26,16 @@ import org.jetbrains.java.decompiler.modules.code.DeadCodeHelper; import org.jetbrains.java.decompiler.modules.decompiler.*; import org.jetbrains.java.decompiler.modules.decompiler.deobfuscator.ExceptionDeobfuscator; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.stats.DummyExitStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructMethod; import java.io.IOException; +import java.util.BitSet; public class MethodProcessorRunnable implements Runnable { @@ -138,7 +143,7 @@ public static RootStatement codeToJava(StructMethod mt, VarProcessor varProc) th proc.processStatement(root, cl); SequenceHelper.condenseSequences(root); - + while (true) { StackVarsProcessor stackProc = new StackVarsProcessor(); stackProc.simplifyStackVars(root, mt, cl); @@ -199,6 +204,8 @@ public static RootStatement codeToJava(StructMethod mt, VarProcessor varProc) th varProc.setVarDefinitions(root); + printMethod(root); + // must be the last invocation, because it makes the statement structure inconsistent // FIXME: new edge type needed LabelHelper.replaceContinueWithBreak(root); @@ -217,4 +224,118 @@ public RootStatement getResult() throws Throwable { public boolean isFinished() { return finished; } + + private static void printMethod(RootStatement root) { + System.out.println("{"); + + for (Object obj : root.getSequentialObjects()) { + if (obj instanceof Statement) { + printStatement((Statement)obj, " "); + } else { + System.out.println(" " + obj.getClass().getSimpleName()); + } + } + printStatement(root.getDummyExit(), " "); + System.out.println("}"); + } + + private static void getOffset(Statement st, BitSet values) { + if (st instanceof DummyExitStatement && ((DummyExitStatement)st).bytecode != null) + values.or(((DummyExitStatement)st).bytecode); + if (st.getExprents() != null) { + for (Exprent e : st.getExprents()) { + e.getBytecodeRange(values); + } + } else { + for (Object obj : st.getSequentialObjects()) { + if (obj instanceof Statement) { + getOffset((Statement)obj, values); + } else if (obj instanceof Exprent) { + ((Exprent)obj).getBytecodeRange(values); + } else { + System.out.println("WTF?" + obj.getClass()); + } + } + } + } + + private static void printStatement(Statement statement, String indent) { + BitSet values = new BitSet(); + getOffset(statement, values); + int start = values.nextSetBit(0); + int end = values.length()-1; + + System.out.println(indent + statement.type + ": (" + start + ", " + end + ") " + statement.getClass().getSimpleName()); + + if (statement.getExprents() != null) { + for(Exprent exp : statement.getExprents()) { + System.out.println(indent + " " + exp.getClass().getSimpleName()); + } + } + /* + for (Object obj : stat.getSequentialObjects()) { + if (obj instanceof Statement) { + Statement st = (Statement)obj; + childVars.addAll(initStatement(st)); + + if (st.type == DoStatement.TYPE_DO) { + DoStatement dost = (DoStatement)st; + if (dost.getLooptype() != DoStatement.LOOP_FOR && + dost.getLooptype() != DoStatement.LOOP_DO) { + currVars.add(dost.getConditionExprent()); + } + } + else if (st.type == DoStatement.TYPE_CATCHALL) { + CatchAllStatement fin = (CatchAllStatement)st; + if (fin.isFinally() && fin.getMonitor() != null) { + currVars.add(fin.getMonitor()); + } + } + } + else if (obj instanceof Exprent) { + currVars.add((Exprent)obj); + } + } + + // children statements + for (Integer index : childVars) { + Integer count = mapCount.get(index); + if (count == null) { + count = new Integer(0); + } + mapCount.put(index, new Integer(count.intValue() + 1)); + } + + condlst = getAllVars(currVars); + } + else { + condlst = getAllVars(stat.getExprents()); + } + + // this statement + for (VarExprent var : condlst) { + mapCount.put(new Integer(var.getIndex()), new Integer(2)); + } + + + HashSet set = new HashSet(mapCount.keySet()); + + // put all variables defined in this statement into the set + for (Entry en : mapCount.entrySet()) { + if (en.getValue().intValue() > 1) { + mapVarDefStatements.put(en.getKey(), stat); + } + } + + mapStatementVars.put(stat.id, set); +*/ + indent += " "; + for (Object obj : statement.getSequentialObjects()) { + if (obj instanceof Statement) { + printStatement((Statement)obj, indent); + } else { + System.out.println(indent + obj.getClass().getSimpleName()); + } + } + } } From d72455431b4c610f5c9768a2f27c7adffcc7f086 Mon Sep 17 00:00:00 2001 From: cpw Date: Sat, 8 Aug 2015 13:37:49 -0400 Subject: [PATCH 02/73] A scoping test, simpler than the complex one, almost passes. --- testData/classes/pkg/TestLVTScoping.class | Bin 0 -> 1024 bytes testData/results/TestLVTScoping.dec | 35 ++++++++++++++++++++++ testData/src/pkg/TestLVTScoping.java | 34 +++++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 testData/classes/pkg/TestLVTScoping.class create mode 100644 testData/results/TestLVTScoping.dec create mode 100644 testData/src/pkg/TestLVTScoping.java diff --git a/testData/classes/pkg/TestLVTScoping.class b/testData/classes/pkg/TestLVTScoping.class new file mode 100644 index 0000000000000000000000000000000000000000..b13a2da2070ddc93780406d9b74262fec5610576 GIT binary patch literal 1024 zcma)4U2hUW6g>l5mZeZWODkY&>ldIFZPgcyNn>I{N?Q{M34L4WNVkPu0!yR+#=H8h*=bU?HzW@CC4Ztp*>+lIA`WKy2T@Kx9qh4z}ear6X z&;(-V=A~Kcns%pj+&Y(SS0M1hvMu+efInYo2xzaIQyGFEMi@&72t=!vEssXMmK@a0 zR#y_d>a@*n!yH&DpKfaInKcwhRj)IbNz#+!-$42W5C2h!}3>JIMdxGRwKbSxf+aUXdK?yDPJn;`k$a zzP8jOB^O_RN71umHq37VXpH#0oONa;u7c}Q@iT%S8Tb(5td3RbGUxE5fNO-AftA_i zA*)qgPSd_1I>zb;7>OGPR|q7s2cK}WIL2*->Gv=*ya$plBS;JbaqgywnkBB{NfT{_ zwFMNJrC2qI9xyZDi~oR6$3uoY{}vGWyMP$U;v`CN=D*{}lKTGy7!v`dnSggw2e#)9 zM71(o&y4Z-3V)Qb;TgY9$_zWtQb3M>dV|>}pJ{6*CBnJ__Sk*Ki Date: Sat, 8 Aug 2015 13:43:47 -0400 Subject: [PATCH 03/73] Add the test to the harness --- test/org/jetbrains/java/decompiler/LVTTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/test/org/jetbrains/java/decompiler/LVTTest.java b/test/org/jetbrains/java/decompiler/LVTTest.java index 2d39827..983368c 100644 --- a/test/org/jetbrains/java/decompiler/LVTTest.java +++ b/test/org/jetbrains/java/decompiler/LVTTest.java @@ -30,4 +30,5 @@ protected Map getDecompilerOptions() { } @Test public void testMatch1() { doTest("pkg/TestLVT"); } + @Test public void testMatch2() { doTest("pkg/TestLVTScoping"); } } From cca06e0f3dd96dd8f0412628e2ee33359c461179 Mon Sep 17 00:00:00 2001 From: cpw Date: Sat, 8 Aug 2015 16:13:27 -0400 Subject: [PATCH 04/73] Some modifications to the printout. Also, fixed a fascinating bug in VarVersionsProcessor where it dumped the original indexes because setVarVersions is called multiple times (seems to be some sort of reparse loop). Preserving an existing origvarstable across the that method seems to have improved accuracy a LOT. --- .../decompiler/main/rels/ClassWrapper.java | 4 ++ .../main/rels/MethodProcessorRunnable.java | 42 +++++++++++++++---- .../modules/decompiler/vars/LVTVariable.java | 4 ++ .../decompiler/vars/LocalVariableTable.java | 12 ++++++ .../modules/decompiler/vars/VarProcessor.java | 21 ++++++++++ .../decompiler/vars/VarVersionsProcessor.java | 1 + .../StructLocalVariableTableAttribute.java | 4 ++ 7 files changed, 79 insertions(+), 9 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java b/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java index 18f05a3..4e86556 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java +++ b/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java @@ -170,9 +170,13 @@ public void init() throws IOException { if (attr != null) { varProc.setDebugVarNames(attr.getMapVarNames()); + varProc.setLVT(attr.getLVT()); } } + MethodProcessorRunnable.printMethod(root, mt.getClassStruct().qualifiedName+"."+mt.getName()+mt.getDescriptor(),varProc); + + DecompilerContext.getLogger().endMethod(); } diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java index dc6f2f0..f0b17a5 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java @@ -26,16 +26,20 @@ import org.jetbrains.java.decompiler.modules.code.DeadCodeHelper; import org.jetbrains.java.decompiler.modules.decompiler.*; import org.jetbrains.java.decompiler.modules.decompiler.deobfuscator.ExceptionDeobfuscator; +import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; import org.jetbrains.java.decompiler.modules.decompiler.stats.DummyExitStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.modules.decompiler.vars.LVTVariable; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructMethod; import java.io.IOException; import java.util.BitSet; +import java.util.List; public class MethodProcessorRunnable implements Runnable { @@ -204,8 +208,6 @@ public static RootStatement codeToJava(StructMethod mt, VarProcessor varProc) th varProc.setVarDefinitions(root); - printMethod(root); - // must be the last invocation, because it makes the statement structure inconsistent // FIXME: new edge type needed LabelHelper.replaceContinueWithBreak(root); @@ -225,17 +227,17 @@ public boolean isFinished() { return finished; } - private static void printMethod(RootStatement root) { - System.out.println("{"); + public static void printMethod(RootStatement root, String name, VarProcessor varProc) { + System.out.println(name + " {"); for (Object obj : root.getSequentialObjects()) { if (obj instanceof Statement) { - printStatement((Statement)obj, " "); + printStatement((Statement)obj, " ",varProc); } else { System.out.println(" " + obj.getClass().getSimpleName()); } } - printStatement(root.getDummyExit(), " "); + printStatement(root.getDummyExit(), " ",varProc); System.out.println("}"); } @@ -259,7 +261,7 @@ private static void getOffset(Statement st, BitSet values) { } } - private static void printStatement(Statement statement, String indent) { + private static void printStatement(Statement statement, String indent, VarProcessor varProc) { BitSet values = new BitSet(); getOffset(statement, values); int start = values.nextSetBit(0); @@ -269,7 +271,7 @@ private static void printStatement(Statement statement, String indent) { if (statement.getExprents() != null) { for(Exprent exp : statement.getExprents()) { - System.out.println(indent + " " + exp.getClass().getSimpleName()); + System.out.println(printExprent(indent + " ", exp,varProc)); } } /* @@ -332,10 +334,32 @@ else if (obj instanceof Exprent) { indent += " "; for (Object obj : statement.getSequentialObjects()) { if (obj instanceof Statement) { - printStatement((Statement)obj, indent); + printStatement((Statement)obj, indent,varProc); + } else if (obj instanceof Exprent) { + System.out.println(printExprent(indent, (Exprent) obj, varProc)); } else { System.out.println(indent + obj.getClass().getSimpleName()); } } } + private static String printExprent(String indent, Exprent exp, VarProcessor varProc) { + StringBuffer sb = new StringBuffer(); + sb.append(indent); + BitSet values = new BitSet(); + exp.getBytecodeRange(values); + sb.append("(").append(values.nextSetBit(0)).append(", ").append(values.length()-1).append(") "); + sb.append(exp.getClass().getSimpleName()); + sb.append(" ").append(exp.id).append(" "); + if (exp instanceof VarExprent) { + VarExprent varExprent = (VarExprent)exp; + int currindex = varExprent.getIndex(); + int origindex = varProc.getRemapped(currindex); + List candidates = varProc.getLVT().getCandidates(origindex); + sb.append("[").append(currindex).append(":").append(origindex).append(", ").append(varExprent.isStack()).append("]").append(candidates); + } else if (exp instanceof AssignmentExprent) { + AssignmentExprent assignmentExprent = (AssignmentExprent)exp; + sb.append("{").append(printExprent(" ",assignmentExprent.getLeft(),varProc)).append(" =").append(printExprent(" ",assignmentExprent.getRight(),varProc)).append("}"); + } + return sb.toString(); + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LVTVariable.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LVTVariable.java index 0533981..13cfa70 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LVTVariable.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LVTVariable.java @@ -67,4 +67,8 @@ public int compareTo(LVTVariable o) { if (o.index < index) return 1; return 0; } + @Override + public String toString() { + return "\'("+index+","+start+")"+desc+(sig!=null ? "<"+sig+"> ":" ")+name+"\'"; + } } \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java index 75f144d..027e816 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java @@ -10,6 +10,7 @@ public class LocalVariableTable { private Map> startpoints; private ArrayList allLVT; private Map mapVarNames; +private Map> mapLVT; public LocalVariableTable(int len) { startpoints = new HashMap>(len); @@ -59,6 +60,7 @@ public Map getMapVarNames() { private void buildNameMap() { Map versions = new HashMap(); mapVarNames = new HashMap(); + mapLVT = new HashMap>(); for (LVTVariable lvt : allLVT) { Integer idx = versions.get(lvt.index); if (idx == null) @@ -66,7 +68,17 @@ private void buildNameMap() { else idx++; versions.put(lvt.index, idx); + List lvtList = mapLVT.get(lvt.index); + if (lvtList == null) { + lvtList = new ArrayList(); + mapLVT.put(lvt.index, lvtList); + } + lvtList.add(lvt); mapVarNames.put(new VarVersionPair(lvt.index, idx.intValue()), lvt.name); } } + +public List getCandidates(int index) { + return mapLVT.get(index); +} } \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java index fbd3455..07b5398 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java @@ -31,10 +31,18 @@ public class VarProcessor { private VarVersionsProcessor varVersions; private final Map thisVars = new HashMap(); private final Set externalVars = new HashSet(); +private LocalVariableTable lvt; public void setVarVersions(RootStatement root) { + Map mapOriginalVarIndices = null; + if (varVersions != null) { + mapOriginalVarIndices = varVersions.getMapOriginalVarIndices(); + } varVersions = new VarVersionsProcessor(); varVersions.setVarVersions(root); + if (mapOriginalVarIndices != null) { + varVersions.getMapOriginalVarIndices().putAll(mapOriginalVarIndices); + } } public void setVarDefinitions(Statement root) { @@ -121,4 +129,17 @@ public Map getThisVars() { public Set getExternalVars() { return externalVars; } + +public void setLVT(LocalVariableTable lvt) { + this.lvt = lvt; +} +public LocalVariableTable getLVT() { + return this.lvt; +} + +public int getRemapped(int index) { + Integer res = varVersions.getMapOriginalVarIndices().get(index); + if (res == null) return index; + return res; +} } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java index 4751052..0de789a 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java @@ -233,6 +233,7 @@ private void setNewVarIndices(VarTypeProcessor typeProcessor, DirectGraph graph) final Map mapVarPaar = new HashMap(); Map mapOriginalVarIndices = new HashMap(); + mapOriginalVarIndices.putAll(this.mapOriginalVarIndices); // map var-version pairs on new var indexes List lst = new ArrayList(mapExprentMinTypes.keySet()); diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java index 4455785..4b00061 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java @@ -68,4 +68,8 @@ public void addLocalVariableTable(StructLocalVariableTableAttribute attr) { public Map getMapVarNames() { return lvt == null ? EMPTY_LVT : lvt.getMapVarNames(); } + +public LocalVariableTable getLVT() { + return lvt; +} } From 67a6f1d47bd5780221f7a545e899430cfc82e9be Mon Sep 17 00:00:00 2001 From: cpw Date: Sat, 8 Aug 2015 17:22:07 -0400 Subject: [PATCH 05/73] Change to track the varversionpair in the "oldnames" table. Track if we have an lvt entry when generating names and trust it's name exactly. --- .../decompiler/vars/LocalVariableTable.java | 2 +- .../modules/decompiler/vars/VarProcessor.java | 17 +++++++++-------- .../decompiler/vars/VarVersionsProcessor.java | 8 ++++---- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java index 027e816..99025a5 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java @@ -64,7 +64,7 @@ private void buildNameMap() { for (LVTVariable lvt : allLVT) { Integer idx = versions.get(lvt.index); if (idx == null) - idx = 0; + idx = 1; else idx++; versions.put(lvt.index, idx); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java index 07b5398..a2a79f5 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java @@ -34,7 +34,7 @@ public class VarProcessor { private LocalVariableTable lvt; public void setVarVersions(RootStatement root) { - Map mapOriginalVarIndices = null; + Map mapOriginalVarIndices = null; if (varVersions != null) { mapOriginalVarIndices = varVersions.getMapOriginalVarIndices(); } @@ -57,7 +57,7 @@ public void setDebugVarNames(Map mapDebugVarNames) { return; } - Map mapOriginalVarIndices = varVersions.getMapOriginalVarIndices(); + Map mapOriginalVarIndices = varVersions.getMapOriginalVarIndices(); List listVars = new ArrayList(mapVarNames.keySet()); Collections.sort(listVars, new Comparator() { @@ -72,18 +72,19 @@ public int compare(VarVersionPair o1, VarVersionPair o2) { for (VarVersionPair pair : listVars) { String name = mapVarNames.get(pair); - Integer index = mapOriginalVarIndices.get(pair.var); - if (index != null) { - VarVersionPair key = new VarVersionPair(index.intValue(), pair.version); + VarVersionPair key = mapOriginalVarIndices.get(pair.var); + boolean lvtName = false; + if (key != null) { if (mapDebugVarNames.containsKey(key)) { name = mapDebugVarNames.get(key); + lvtName = true; } } Integer counter = mapNames.get(name); mapNames.put(name, counter == null ? counter = new Integer(0) : ++counter); - if (counter > 0) { + if (counter > 0 && !lvtName) { name += String.valueOf(counter); } @@ -138,8 +139,8 @@ public LocalVariableTable getLVT() { } public int getRemapped(int index) { - Integer res = varVersions.getMapOriginalVarIndices().get(index); + VarVersionPair res = varVersions.getMapOriginalVarIndices().get(index); if (res == null) return index; - return res; + return res.var; } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java index 0de789a..eae4cba 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java @@ -34,7 +34,7 @@ public class VarVersionsProcessor { - private Map mapOriginalVarIndices = new HashMap(); + private Map mapOriginalVarIndices = new HashMap(); private VarTypeProcessor typeProcessor; public void setVarVersions(RootStatement root) { @@ -232,7 +232,7 @@ private void setNewVarIndices(VarTypeProcessor typeProcessor, DirectGraph graph) CounterContainer counters = DecompilerContext.getCounterContainer(); final Map mapVarPaar = new HashMap(); - Map mapOriginalVarIndices = new HashMap(); + Map mapOriginalVarIndices = new HashMap(); mapOriginalVarIndices.putAll(this.mapOriginalVarIndices); // map var-version pairs on new var indexes @@ -259,7 +259,7 @@ public int compare(VarVersionPair o1, VarVersionPair o2) { } mapVarPaar.put(pair, newIndex); - mapOriginalVarIndices.put(newIndex, pair.var); + mapOriginalVarIndices.put(newIndex, pair); } } @@ -316,7 +316,7 @@ public void setVarFinal(VarVersionPair pair, int finalType) { typeProcessor.getMapFinalVars().put(pair, finalType); } - public Map getMapOriginalVarIndices() { + public Map getMapOriginalVarIndices() { return mapOriginalVarIndices; } } From b75a7c5fc84e6da8582a01e6a772572eaf1dcdc4 Mon Sep 17 00:00:00 2001 From: cpw Date: Sat, 8 Aug 2015 19:43:37 -0400 Subject: [PATCH 06/73] Sort by version (it seems that it increases as the visitors visit the various methods), and use that to align versions with different instances of the same variable. Still some tweaks need to be made, but it's starting to look promising. --- .../decompiler/vars/LocalVariableTable.java | 11 +++---- .../modules/decompiler/vars/VarProcessor.java | 29 +++++++++++++++---- .../StructLocalVariableTableAttribute.java | 5 ++-- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java index 99025a5..e5fbec8 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java @@ -9,7 +9,6 @@ public class LocalVariableTable { private Map> startpoints; private ArrayList allLVT; - private Map mapVarNames; private Map> mapLVT; public LocalVariableTable(int len) { @@ -33,7 +32,7 @@ public void mergeLVTs(LocalVariableTable otherLVT) { mine.merge(other); } } - mapVarNames = null; // Invalidate the cache and rebuild it. + mapLVT = null; // Invalidate the cache and rebuild it. } public LVTVariable find(Integer index, List offsets) { @@ -51,15 +50,14 @@ public LVTVariable find(Integer index, List offsets) { return null; } - public Map getMapVarNames() { - if (mapVarNames == null) + public Map> getMapVarNames() { + if (mapLVT == null) buildNameMap(); - return mapVarNames; + return mapLVT; } private void buildNameMap() { Map versions = new HashMap(); - mapVarNames = new HashMap(); mapLVT = new HashMap>(); for (LVTVariable lvt : allLVT) { Integer idx = versions.get(lvt.index); @@ -74,7 +72,6 @@ private void buildNameMap() { mapLVT.put(lvt.index, lvtList); } lvtList.add(lvt); - mapVarNames.put(new VarVersionPair(lvt.index, idx.intValue()), lvt.name); } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java index a2a79f5..ccd1bd6 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java @@ -52,7 +52,7 @@ public void setVarDefinitions(Statement root) { new VarDefinitionHelper(root, mt, this).setVarDefinitions(); } - public void setDebugVarNames(Map mapDebugVarNames) { + public void setDebugVarNames(Map> mapDebugVarNames) { if (varVersions == null) { return; } @@ -68,16 +68,35 @@ public int compare(VarVersionPair o1, VarVersionPair o2) { }); Map mapNames = new HashMap(); - + Map> indexedPairs = new HashMap>(); + Comparator vvpVersionComparator = new Comparator() { + @Override + public int compare(VarVersionPair o1, VarVersionPair o2) { + return o1.version - o2.version; + } + }; + for (Entry vvp : mapOriginalVarIndices.entrySet()) { + SortedSet set = indexedPairs.get(vvp.getValue().var); + if (set == null) { + set = new TreeSet(vvpVersionComparator); + indexedPairs.put(vvp.getValue().var, set); + } + set.add(vvp.getValue()); + } for (VarVersionPair pair : listVars) { String name = mapVarNames.get(pair); VarVersionPair key = mapOriginalVarIndices.get(pair.var); + boolean lvtName = false; if (key != null) { - if (mapDebugVarNames.containsKey(key)) { - name = mapDebugVarNames.get(key); - lvtName = true; + if (indexedPairs.containsKey(key.var)) { + int veridx = indexedPairs.get(key.var).headSet(key).size(); + List list = mapDebugVarNames.get(key.var); + if (list.size()>veridx) { + name = list.get(veridx).name; + lvtName = true; + } } } diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java index 4b00061..a983b27 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java @@ -23,6 +23,7 @@ import java.io.IOException; import java.util.Collections; +import java.util.List; import java.util.Map; /* @@ -37,7 +38,7 @@ */ public class StructLocalVariableTableAttribute extends StructGeneralAttribute { - private Map EMPTY_LVT = Collections.emptyMap(); + private Map> EMPTY_LVT = Collections.emptyMap(); private LocalVariableTable lvt; @Override @@ -65,7 +66,7 @@ public void addLocalVariableTable(StructLocalVariableTableAttribute attr) { attr.lvt = lvt; } - public Map getMapVarNames() { + public Map> getMapVarNames() { return lvt == null ? EMPTY_LVT : lvt.getMapVarNames(); } From 1f19690b00959c237600d3fac5ae0a28d8e4045f Mon Sep 17 00:00:00 2001 From: cpw Date: Sun, 9 Aug 2015 11:58:54 -0400 Subject: [PATCH 07/73] Ignore variables without an LVT entry. They're likely dummy vars generated by FF for exceptions, little info is missing. Also, don't exception on empty blocks, they're just empty blocks! --- .../decompiler/main/rels/ClassWrapper.java | 2 +- .../main/rels/MethodProcessorRunnable.java | 5 +- .../modules/decompiler/vars/VarProcessor.java | 4 +- .../MinecraftDecompilationTest.java | 125 ++++++++++++++++++ 4 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 test/org/jetbrains/java/decompiler/MinecraftDecompilationTest.java diff --git a/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java b/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java index 4e86556..5a35cc8 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java +++ b/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java @@ -169,8 +169,8 @@ public void init() throws IOException { StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE); if (attr != null) { - varProc.setDebugVarNames(attr.getMapVarNames()); varProc.setLVT(attr.getLVT()); + varProc.setDebugVarNames(attr.getMapVarNames()); } } diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java index f0b17a5..388e76a 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java @@ -229,7 +229,10 @@ public boolean isFinished() { public static void printMethod(RootStatement root, String name, VarProcessor varProc) { System.out.println(name + " {"); - + if (root == null || root.getSequentialObjects() == null) { + System.out.println("}"); + return; + } for (Object obj : root.getSequentialObjects()) { if (obj instanceof Statement) { printStatement((Statement)obj, " ",varProc); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java index ccd1bd6..6b15a12 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java @@ -93,9 +93,11 @@ public int compare(VarVersionPair o1, VarVersionPair o2) { if (indexedPairs.containsKey(key.var)) { int veridx = indexedPairs.get(key.var).headSet(key).size(); List list = mapDebugVarNames.get(key.var); - if (list.size()>veridx) { + if (list != null && list.size()>veridx) { name = list.get(veridx).name; lvtName = true; + } else if (list == null) { + // we're an exception type, probably. let's just fall through } } } diff --git a/test/org/jetbrains/java/decompiler/MinecraftDecompilationTest.java b/test/org/jetbrains/java/decompiler/MinecraftDecompilationTest.java new file mode 100644 index 0000000..c7e781e --- /dev/null +++ b/test/org/jetbrains/java/decompiler/MinecraftDecompilationTest.java @@ -0,0 +1,125 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.java.decompiler; + +import org.hamcrest.Matchers; +import org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler; +import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; +import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.*; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +public class MinecraftDecompilationTest { + private DecompilerTestFixture fixture; + + private static final String MC_JAR = "minecraft_ff_in.jar"; + @Before + public void setUp() throws IOException { + fixture = new DecompilerTestFixture(); + // -din=1 -rbr=0 -dgs=1 -asc=1 -rsy=0 + Map mcFFOptions = new HashMap() {{ + put(IFernflowerPreferences.DECOMPILE_INNER,"1"); + put(IFernflowerPreferences.REMOVE_BRIDGE, "0"); + put(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES,"1"); + put(IFernflowerPreferences.ASCII_STRING_CHARACTERS,"1"); + put(IFernflowerPreferences.REMOVE_SYNTHETIC,"0"); + }}; + fixture.setUp(mcFFOptions); + if (!new File(fixture.getTestDataDir(), MC_JAR).exists()) { + throw new RuntimeException("Missing "+MC_JAR+" in testData dir - aborting"); + } + } + + @After + public void tearDown() { +// fixture.tearDown(); +// fixture = null; + } + +// @Test +// public void testDirectory() { +// File classes = new File(fixture.getTempDir(), "classes"); +// unpack(new File(fixture.getTestDataDir(), "mc-fernflower-in.jar"), classes); +// +// ConsoleDecompiler decompiler = fixture.getDecompiler(); +// decompiler.addSpace(classes, true); +// decompiler.decompileContext(); +// +// compareDirectories(new File(fixture.getTestDataDir(), "bulk"), fixture.getTargetDir()); +// } + + @Test + public void testJar() { + ConsoleDecompiler decompiler = fixture.getDecompiler(); + decompiler.addSpace(new File(fixture.getTestDataDir(), MC_JAR), true); + decompiler.decompileContext(); + + File unpacked = new File(fixture.getTempDir(), "unpacked"); + unpack(new File(fixture.getTargetDir(), "bulk.jar"), unpacked); + +// compareDirectories(new File(fixture.getTestDataDir(), "bulk"), unpacked); + } + + private static void unpack(File archive, File targetDir) { + try { + ZipFile zip = new ZipFile(archive); + try { + Enumeration entries = zip.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + if (!entry.isDirectory()) { + File file = new File(targetDir, entry.getName()); + assertTrue(file.getParentFile().mkdirs() || file.getParentFile().isDirectory()); + InputStream in = zip.getInputStream(entry); + OutputStream out = new FileOutputStream(file); + InterpreterUtil.copyStream(in, out); + out.close(); + in.close(); + } + } + } + finally { + zip.close(); + } + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static void compareDirectories(File expected, File actual) { + String[] expectedList = expected.list(); + String[] actualList = actual.list(); + assertThat(actualList, Matchers.arrayContainingInAnyOrder(expectedList)); + for (String name : expectedList) { + File child = new File(expected, name); + if (child.isDirectory()) { + compareDirectories(child, new File(actual, name)); + } + } + } +} From 5f699acc70d8e40f758737ae88aeddecc07edc8c Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Sun, 9 Aug 2015 15:04:04 -0700 Subject: [PATCH 08/73] TryCatch statements will now use the correct variable index for their exception variable. Some small cleanups and NPE fix. --- .../decompiler/main/ClassesProcessor.java | 1 - .../main/rels/MethodProcessorRunnable.java | 63 +------------------ .../decompiler/stats/CatchAllStatement.java | 11 +++- .../decompiler/stats/CatchStatement.java | 10 ++- .../modules/decompiler/vars/VarProcessor.java | 13 ++-- .../StructLocalVariableTableAttribute.java | 1 - .../TestClassSimpleBytecodeMapping.dec | 4 +- testData/results/TestTryCatchFinally.dec | 12 ++-- 8 files changed, 37 insertions(+), 78 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java index 7f75a0c..0060021 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java @@ -32,7 +32,6 @@ import org.jetbrains.java.decompiler.struct.StructContext; import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.attr.StructEnclosingMethodAttribute; -import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; import org.jetbrains.java.decompiler.struct.attr.StructInnerClassesAttribute; import org.jetbrains.java.decompiler.struct.attr.StructInnerClassesAttribute.InnerClassInfo; import org.jetbrains.java.decompiler.struct.gen.VarType; diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java index 388e76a..9542a80 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java @@ -17,7 +17,6 @@ import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.InstructionSequence; -import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; @@ -277,63 +276,6 @@ private static void printStatement(Statement statement, String indent, VarProces System.out.println(printExprent(indent + " ", exp,varProc)); } } - /* - for (Object obj : stat.getSequentialObjects()) { - if (obj instanceof Statement) { - Statement st = (Statement)obj; - childVars.addAll(initStatement(st)); - - if (st.type == DoStatement.TYPE_DO) { - DoStatement dost = (DoStatement)st; - if (dost.getLooptype() != DoStatement.LOOP_FOR && - dost.getLooptype() != DoStatement.LOOP_DO) { - currVars.add(dost.getConditionExprent()); - } - } - else if (st.type == DoStatement.TYPE_CATCHALL) { - CatchAllStatement fin = (CatchAllStatement)st; - if (fin.isFinally() && fin.getMonitor() != null) { - currVars.add(fin.getMonitor()); - } - } - } - else if (obj instanceof Exprent) { - currVars.add((Exprent)obj); - } - } - - // children statements - for (Integer index : childVars) { - Integer count = mapCount.get(index); - if (count == null) { - count = new Integer(0); - } - mapCount.put(index, new Integer(count.intValue() + 1)); - } - - condlst = getAllVars(currVars); - } - else { - condlst = getAllVars(stat.getExprents()); - } - - // this statement - for (VarExprent var : condlst) { - mapCount.put(new Integer(var.getIndex()), new Integer(2)); - } - - - HashSet set = new HashSet(mapCount.keySet()); - - // put all variables defined in this statement into the set - for (Entry en : mapCount.entrySet()) { - if (en.getValue().intValue() > 1) { - mapVarDefStatements.put(en.getKey(), stat); - } - } - - mapStatementVars.put(stat.id, set); -*/ indent += " "; for (Object obj : statement.getSequentialObjects()) { if (obj instanceof Statement) { @@ -357,8 +299,9 @@ private static String printExprent(String indent, Exprent exp, VarProcessor varP VarExprent varExprent = (VarExprent)exp; int currindex = varExprent.getIndex(); int origindex = varProc.getRemapped(currindex); - List candidates = varProc.getLVT().getCandidates(origindex); - sb.append("[").append(currindex).append(":").append(origindex).append(", ").append(varExprent.isStack()).append("]").append(candidates); + sb.append("[").append(currindex).append(":").append(origindex).append(", ").append(varExprent.isStack()).append("]"); + if (varProc.getLVT() != null) + sb.append(varProc.getLVT().getCandidates(origindex)); } else if (exp instanceof AssignmentExprent) { AssignmentExprent assignmentExprent = (AssignmentExprent)exp; sb.append("{").append(printExprent(" ",assignmentExprent.getLeft(),varProc)).append(" =").append(printExprent(" ",assignmentExprent.getRight(),varProc)).append("}"); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java index 812ed10..d5b432c 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java @@ -16,6 +16,7 @@ package org.jetbrains.java.decompiler.modules.decompiler.stats; import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.TextBuffer; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; @@ -68,7 +69,15 @@ private CatchAllStatement(Statement head, Statement handler) { } } - vars.add(new VarExprent(DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER), + int varIndex = -1; + BasicBlock block = handler.getBasichead().getBlock(); + if (!block.getSeq().isEmpty() && block.getInstruction(0).opcode == CodeConstants.opc_astore) { + varIndex = block.getInstruction(0).getOperand(0); + } + else { + varIndex = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER); + } + vars.add(new VarExprent(varIndex, new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Throwable"), (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR))); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java index 8d56042..fda5ed3 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java @@ -60,7 +60,15 @@ private CatchStatement(Statement head, Statement next, HashSet setHan stats.addWithKey(stat, stat.id); exctstrings.add(new ArrayList(edge.getExceptions())); - vars.add(new VarExprent(DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER), + int varIndex = -1; + BasicBlock block = stat.getBasichead().getBlock(); + if (!block.getSeq().isEmpty() && block.getInstruction(0).opcode == CodeConstants.opc_astore) { + varIndex = block.getInstruction(0).getOperand(0); + } + else { + varIndex = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER); + } + vars.add(new VarExprent(varIndex, new VarType(CodeConstants.TYPE_OBJECT, 0, edge.getExceptions().get(0)), // FIXME: for now simply the first type. Should get the first common superclass when possible. (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR))); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java index 6b15a12..ef07c6b 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java @@ -152,16 +152,17 @@ public Set getExternalVars() { return externalVars; } -public void setLVT(LocalVariableTable lvt) { + public void setLVT(LocalVariableTable lvt) { this.lvt = lvt; -} -public LocalVariableTable getLVT() { + } + + public LocalVariableTable getLVT() { return this.lvt; -} + } -public int getRemapped(int index) { + public int getRemapped(int index) { VarVersionPair res = varVersions.getMapOriginalVarIndices().get(index); if (res == null) return index; return res.var; -} + } } diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java index a983b27..a654359 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java @@ -17,7 +17,6 @@ import org.jetbrains.java.decompiler.modules.decompiler.vars.LVTVariable; import org.jetbrains.java.decompiler.modules.decompiler.vars.LocalVariableTable; -import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.util.DataInputFullStream; diff --git a/testData/results/TestClassSimpleBytecodeMapping.dec b/testData/results/TestClassSimpleBytecodeMapping.dec index 1a2532f..4a2f333 100644 --- a/testData/results/TestClassSimpleBytecodeMapping.dec +++ b/testData/results/TestClassSimpleBytecodeMapping.dec @@ -21,8 +21,8 @@ public class TestClassSimpleBytecodeMapping { public void test2(String var1) { try { Integer.parseInt(var1);// 34 - } catch (Exception var6) {// 35 - System.out.println(var6);// 36 + } catch (Exception var2) {// 35 + System.out.println(var2);// 36 } finally { System.out.println("Finally");// 38 } diff --git a/testData/results/TestTryCatchFinally.dec b/testData/results/TestTryCatchFinally.dec index 772a76e..fbe5782 100644 --- a/testData/results/TestTryCatchFinally.dec +++ b/testData/results/TestTryCatchFinally.dec @@ -4,10 +4,10 @@ public class TestTryCatchFinally { public void test1(String var1) { try { System.out.println("sout1");// 24 - } catch (Exception var9) { + } catch (Exception var2) { try { System.out.println("sout2");// 27 - } catch (Exception var8) {// 28 + } catch (Exception var3) {// 28 ; } } finally { @@ -28,10 +28,10 @@ public class TestTryCatchFinally { public int test(String var1) { try { - int var2 = Integer.parseInt(var1);// 51 - return var2; - } catch (Exception var6) {// 52 - System.out.println("Error" + var6);// 53 + int var5 = Integer.parseInt(var1);// 51 + return var5; + } catch (Exception var2) {// 52 + System.out.println("Error" + var2);// 53 } finally { System.out.println("Finally");// 55 } From 143b1e688e6e73e0f54be08e2c381c740c75fd13 Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Thu, 13 Aug 2015 16:03:24 -0700 Subject: [PATCH 09/73] Add enhanced ForEach loop detection and re-introduce the DotExporter for debugging. --- .../decompiler/main/rels/ClassWrapper.java | 1 - .../main/rels/NestedClassProcessor.java | 2 + .../modules/decompiler/MergeHelper.java | 210 +++++++++++++++- .../modules/decompiler/exps/VarExprent.java | 5 + .../sforms/FlattenStatementsHelper.java | 7 +- .../sforms/SSAConstructorSparseEx.java | 17 +- .../sforms/SSAUConstructorSparseEx.java | 16 +- .../modules/decompiler/stats/DoStatement.java | 12 + .../decompiler/vars/VarDefinitionHelper.java | 10 + .../modules/decompiler/vars/VarProcessor.java | 2 +- .../decompiler/vars/VarVersionsProcessor.java | 2 + .../java/decompiler/util/DotExporter.java | 238 ++++++++++++++++++ 12 files changed, 498 insertions(+), 24 deletions(-) create mode 100644 src/org/jetbrains/java/decompiler/util/DotExporter.java diff --git a/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java b/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java index 5a35cc8..01b9292 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java +++ b/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java @@ -176,7 +176,6 @@ public void init() throws IOException { MethodProcessorRunnable.printMethod(root, mt.getClassStruct().qualifiedName+"."+mt.getName()+mt.getDescriptor(),varProc); - DecompilerContext.getLogger().endMethod(); } diff --git a/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java b/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java index 9aedf22..c6eda1b 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java @@ -301,6 +301,8 @@ private static void computeLocalVarsAndDefinitions(final ClassNode node) { if (meth.root != null) { // neither abstract, nor native DirectGraph graph = meth.getOrBuildGraph(); + org.jetbrains.java.decompiler.util.DotExporter.toDotFile(graph, meth.methodStruct, "computeLocalVars"); + graph.iterateExprents(new DirectGraph.ExprentIterator() { public int processExprent(Exprent exprent) { List lst = exprent.getAllExprents(true); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java index 71d3d79..d994c26 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java @@ -18,8 +18,13 @@ import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; +import org.jetbrains.java.decompiler.modules.decompiler.exps.ArrayExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.IfExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; import org.jetbrains.java.decompiler.modules.decompiler.stats.*; import java.util.ArrayList; @@ -63,6 +68,8 @@ private static boolean enhanceLoop(DoStatement stat) { if (matchWhile(stat)) { // identify a for loop - subtype of while matchFor(stat); + // identify for each loop, + matchForEach(stat); } else { // identify a do{}while loop @@ -72,6 +79,7 @@ private static boolean enhanceLoop(DoStatement stat) { break; case DoStatement.LOOP_WHILE: matchFor(stat); + matchForEach(stat); } return (stat.getLooptype() != oldloop); @@ -366,17 +374,193 @@ private static boolean matchFor(DoStatement stat) { stat.setIncExprent(lastData.getExprents().remove(lastData.getExprents().size() - 1)); } - if (lastData.getExprents().isEmpty()) { - List lst = lastData.getAllSuccessorEdges(); - if (!lst.isEmpty()) { - lastData.removeSuccessor(lst.get(0)); + cleanEmptyStatements(stat, lastData); + + return true; + } + + private static boolean matchForEach(DoStatement stat) { + AssignmentExprent firstDoExprent = null, initDoExprent = null, initCopyExprent = null; + Statement firstData, preData = null; + + // search for an initializing exprent + Statement current = stat; + while (true) { + Statement parent = current.getParent(); + if (parent == null) { + break; + } + + if (parent.type == Statement.TYPE_SEQUENCE) { + if (current == parent.getFirst()) { + current = parent; + } + else { + preData = current.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD).get(0); + preData = getLastDirectData(preData); + if (preData != null && !preData.getExprents().isEmpty()) { + Exprent exprent = preData.getExprents().get(preData.getExprents().size() - 1); + if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { + initDoExprent = (AssignmentExprent)exprent; + if (preData.getExprents().size() >= 2) { + exprent = preData.getExprents().get(preData.getExprents().size() - 2); + if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { + initCopyExprent = (AssignmentExprent)exprent; + } + } + } + } + break; + } + } + else { + break; } - removeLastEmptyStatement(stat, lastData); } + firstData = getFirstDirectData(stat.getFirst()); + if (firstData != null && firstData.getExprents().get(0).type == Exprent.EXPRENT_ASSIGNMENT) { + firstDoExprent = (AssignmentExprent)firstData.getExprents().get(0); + } + + if (stat.getLooptype() == DoStatement.LOOP_WHILE && initDoExprent != null && firstDoExprent != null) { + if (initDoExprent.type == Exprent.EXPRENT_ASSIGNMENT && + isInvoke(((AssignmentExprent)initDoExprent).getRight(), null, "iterator", "()Ljava/util/Iterator;")) { + + if (!isInvoke(drillNots(stat.getConditionExprent()), "java/util/Iterator", "hasNext", "()Z") || + firstDoExprent.type != Exprent.EXPRENT_ASSIGNMENT) { + return false; + } + + AssignmentExprent ass = (AssignmentExprent)firstDoExprent; + if (!isInvoke(ass.getRight(), "java/util/Iterator", "next", "()Ljava/lang/Object;") || + ass.getLeft().type != Exprent.EXPRENT_VAR) { + return false; + } + + InvocationExprent holder = (InvocationExprent)((AssignmentExprent)initDoExprent).getRight(); + stat.setLooptype(DoStatement.LOOP_FOREACH); + stat.setInitExprent(ass.getLeft()); + stat.setIncExprent(holder.getInstance()); + preData.getExprents().remove(initDoExprent); + firstData.getExprents().remove(firstDoExprent); + } + } + else if (stat.getLooptype() == DoStatement.LOOP_FOR) { + if (isType(stat.getInitExprent(), Exprent.EXPRENT_ASSIGNMENT) && + isInvoke(((AssignmentExprent)stat.getInitExprent()).getRight(), null, "iterator", "()Ljava/util/Iterator;") && + isType(stat.getConditionExprent(), Exprent.EXPRENT_FUNCTION)) { + + if (!isInvoke(drillNots(stat.getConditionExprent()), "java/util/Iterator", "hasNext", "()Z") || + !isType(stat.getIncExprent(), Exprent.EXPRENT_ASSIGNMENT)) { + return false; + } + + AssignmentExprent ass = (AssignmentExprent)stat.getIncExprent(); + if (!isInvoke(ass.getRight(), "java/util/Iterator", "next", "()Ljava/lang/Object;") || + ass.getLeft().type != Exprent.EXPRENT_VAR) { + return false; + } + + InvocationExprent holder = (InvocationExprent)((AssignmentExprent)stat.getInitExprent()).getRight(); + stat.setLooptype(DoStatement.LOOP_FOREACH); + stat.setInitExprent(ass.getLeft()); + stat.setIncExprent(holder.getInstance()); + } + else if (initDoExprent != null && initDoExprent.getRight().type == Exprent.EXPRENT_FUNCTION) { + if (firstDoExprent == null || + firstDoExprent.getRight().type != Exprent.EXPRENT_ARRAY || + firstDoExprent.getLeft().type != Exprent.EXPRENT_VAR || + !isType(stat.getIncExprent(), Exprent.EXPRENT_FUNCTION) || + !isType(stat.getInitExprent(), Exprent.EXPRENT_ASSIGNMENT)) { + return false; + } + + FunctionExprent funcRight = (FunctionExprent)initDoExprent.getRight(); + FunctionExprent funcInc = (FunctionExprent)stat.getIncExprent(); + ArrayExprent arr = (ArrayExprent)firstDoExprent.getRight(); + + if (funcRight.getFuncType() != FunctionExprent.FUNCTION_ARRAY_LENGTH || + (funcInc.getFuncType() != FunctionExprent.FUNCTION_PPI && funcInc.getFuncType() != FunctionExprent.FUNCTION_IPP) || + arr.getIndex().type != Exprent.EXPRENT_VAR || + arr.getArray().type != Exprent.EXPRENT_VAR) { + return false; + } + + VarExprent index = (VarExprent)arr.getIndex(); + VarExprent array = (VarExprent)arr.getArray(); + VarExprent counter = (VarExprent)funcInc.getLstOperands().get(0); + + if (counter.getIndex() != index.getIndex() || + counter.getVersion() != index.getVersion()) { + return false; + } + + stat.setLooptype(DoStatement.LOOP_FOREACH); + stat.setInitExprent(firstDoExprent.getLeft()); + stat.setIncExprent(funcRight.getLstOperands().get(0)); + preData.getExprents().remove(initDoExprent); + firstData.getExprents().remove(firstDoExprent); + + if (initCopyExprent != null && initCopyExprent.getLeft().type == Exprent.EXPRENT_VAR) { + VarExprent copy = (VarExprent)initCopyExprent.getLeft(); + if (copy.getIndex() == array.getIndex() && copy.getVersion() == array.getVersion()) { + preData.getExprents().remove(initCopyExprent); + stat.setIncExprent(initCopyExprent.getRight()); + } + } + } + } + + cleanEmptyStatements(stat, firstData); + return true; } + private static boolean isType(Exprent exp, int type) { //This is just a helper macro, Wish java had real macros. + return exp != null && exp.type == type; + } + + private static boolean isInvoke(Exprent exp, String cls, String method, String desc) { + if (!isType(exp, Exprent.EXPRENT_INVOCATION)) { + return false; + } + InvocationExprent invoc = (InvocationExprent)exp; + if (cls != null && !cls.equals(invoc.getClassname())) { + return false; + } + return method.equals(invoc.getName()) && desc.equals(invoc.getStringDescriptor()); + } + private static Exprent drillNots(Exprent exp) { + while (true) { + if (exp.type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent fun = (FunctionExprent)exp; + if (fun.getFuncType() == FunctionExprent.FUNCTION_BOOL_NOT) { + exp = fun.getLstOperands().get(0); + } + else if (fun.getFuncType() == FunctionExprent.FUNCTION_EQ || + fun.getFuncType() == FunctionExprent.FUNCTION_NE) { + return fun.getLstOperands().get(0); + } + else { + return null; + } + } + else { + return null; + } + } + } + + private static void cleanEmptyStatements(DoStatement dostat, Statement stat) { + if (stat != null && stat.getExprents().isEmpty()) { + List lst = stat.getAllSuccessorEdges(); + if (!lst.isEmpty()) { + stat.removeSuccessor(lst.get(0)); + } + removeLastEmptyStatement(dostat, stat); + } + } private static void removeLastEmptyStatement(DoStatement dostat, Statement stat) { if (stat == dostat.getFirst()) { @@ -418,4 +602,20 @@ private static Statement getLastDirectData(Statement stat) { } return null; } + + private static Statement getFirstDirectData(Statement stat) { + if (stat.getExprents() != null && !stat.getExprents().isEmpty()) { + return stat; + } + + if (stat.type == Statement.TYPE_SEQUENCE) { + for (Statement tmp : stat.getStats()) { + Statement ret = getFirstDirectData(tmp); + if (ret != null) { + return ret; + } + } + } + return null; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java index f071933..969a03e 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java @@ -217,4 +217,9 @@ public boolean match(MatchNode matchNode, MatchEngine engine) { return true; } + @Override + public String toString() { + return "var_" + index + "_" + version; + } + } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java index 971ace5..d88480e 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java @@ -16,8 +16,12 @@ package org.jetbrains.java.decompiler.modules.decompiler.sforms; import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; +import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; import org.jetbrains.java.decompiler.modules.decompiler.stats.*; +import org.jetbrains.java.decompiler.struct.gen.VarType; import java.util.*; import java.util.Map.Entry; @@ -215,6 +219,7 @@ public StatementStackEntry(Statement statement, LinkedList stackFina sourcenode = node; break; case DoStatement.LOOP_FOR: + case DoStatement.LOOP_FOREACH: DirectNode nodeinit = new DirectNode(DirectNode.NODE_INIT, stat, stat.id + "_init"); if (dostat.getInitExprent() != null) { nodeinit.exprents = dostat.getInitExprentList(); @@ -222,7 +227,7 @@ public StatementStackEntry(Statement statement, LinkedList stackFina graph.nodes.putWithKey(nodeinit, nodeinit.id); DirectNode nodecond = new DirectNode(DirectNode.NODE_CONDITION, stat, stat.id + "_cond"); - nodecond.exprents = dostat.getConditionExprentList(); + nodecond.exprents = (stat.type == DoStatement.LOOP_FOREACH ? null : dostat.getConditionExprentList()); graph.nodes.putWithKey(nodecond, nodecond.id); DirectNode nodeinc = new DirectNode(DirectNode.NODE_INCREMENT, stat, stat.id + "_inc"); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAConstructorSparseEx.java b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAConstructorSparseEx.java index 76a973f..7f4118c 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAConstructorSparseEx.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAConstructorSparseEx.java @@ -59,6 +59,7 @@ public class SSAConstructorSparseEx { // var, version private final HashMap lastversion = new HashMap(); + @Deprecated // Collection containing all vars for catch blocks and parameters. Not used for anything... private final List startVars = new ArrayList(); // set factory @@ -69,11 +70,9 @@ 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();} + org.jetbrains.java.decompiler.util.DotExporter.toDotFile(dgraph, mt, "ssaSplitVariables"); - HashSet setInit = new HashSet(); + List setInit = new ArrayList(); //Important: HashSets have undefined order, so use a ordered list. for (int i = 0; i < 64; i++) { setInit.add(i); } @@ -84,20 +83,19 @@ public void splitVariables(RootStatement root, StructMethod mt) { setCatchMaps(root, dgraph, flatthelper); + int itteration = 1; HashSet updated = new HashSet(); do { // System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava()); - ssaStatements(dgraph, updated); + ssaStatements(dgraph, updated, mt, itteration++); // System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava()); } while (!updated.isEmpty()); } - private void ssaStatements(DirectGraph dgraph, HashSet updated) { + private void ssaStatements(DirectGraph dgraph, HashSet updated, StructMethod mt, int itteration) { - // try { - // DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr1_my.dot")); - // } catch(Exception ex) {ex.printStackTrace();} + org.jetbrains.java.decompiler.util.DotExporter.toDotFile(dgraph, mt, "ssaStatements_" + itteration, outVarVersions); for (DirectNode node : dgraph.nodes) { @@ -523,6 +521,7 @@ public HashMap> getPhi() { return phi; } + @Deprecated // Collection containing all vars for catch blocks and parameters. public List getStartVars() { return startVars; } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAUConstructorSparseEx.java b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAUConstructorSparseEx.java index 95cefcf..9ba8195 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAUConstructorSparseEx.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAUConstructorSparseEx.java @@ -88,7 +88,9 @@ public void splitVariables(RootStatement root, StructMethod mt) { FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); DirectGraph dgraph = flatthelper.buildDirectGraph(root); - HashSet setInit = new HashSet(); + org.jetbrains.java.decompiler.util.DotExporter.toDotFile(dgraph, mt, "ssauSplitVariables"); + + List setInit = new ArrayList(); //Important: HashSets have undefined order, so use a ordered list. for (int i = 0; i < 64; i++) { setInit.add(i); } @@ -98,25 +100,25 @@ public void splitVariables(RootStatement root, StructMethod mt) { setCatchMaps(root, dgraph, flatthelper); - // try { - // DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr12_my.dot")); - // } catch(Exception ex) {ex.printStackTrace();} + int itteration = 1; HashSet updated = new HashSet(); do { // System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava()); - ssaStatements(dgraph, updated, false); + ssaStatements(dgraph, updated, false, mt, itteration++); // System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava()); } while (!updated.isEmpty()); - ssaStatements(dgraph, updated, true); + ssaStatements(dgraph, updated, true, mt, itteration); ssuversions.initDominators(); } - private void ssaStatements(DirectGraph dgraph, HashSet updated, boolean calcLiveVars) { + private void ssaStatements(DirectGraph dgraph, HashSet updated, boolean calcLiveVars, StructMethod mt, int itteration) { + + org.jetbrains.java.decompiler.util.DotExporter.toDotFile(dgraph, mt, "ssauStatements_" + itteration); for (DirectNode node : dgraph.nodes) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java index ef5ea76..8359284 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java @@ -19,6 +19,7 @@ import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; +import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import java.util.ArrayList; @@ -31,6 +32,7 @@ public class DoStatement extends Statement { public static final int LOOP_DOWHILE = 1; public static final int LOOP_WHILE = 2; public static final int LOOP_FOR = 3; + public static final int LOOP_FOREACH = 4; private int looptype; @@ -135,6 +137,13 @@ public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { buf.append(ExprProcessor.jmpWrapper(first, indent + 1, true, tracer)); buf.appendIndent(indent).append("}").appendLineSeparator(); tracer.incrementCurrentSourceLine(); + break; + case LOOP_FOREACH: + buf.appendIndent(indent).append("for(").append(initExprent.get(0).toJava(indent, tracer)); + buf.append(" : ").append(incExprent.get(0).toJava(indent, tracer)).append(") {").appendLineSeparator(); + tracer.incrementCurrentSourceLine(); + buf.append(ExprProcessor.jmpWrapper(first, indent + 1, true, tracer)); + buf.appendIndent(indent).append("}").appendLineSeparator(); } return buf; @@ -151,6 +160,9 @@ public List getSequentialObjects() { } case LOOP_WHILE: lst.add(getConditionExprent()); + break; + case LOOP_FOREACH: + lst.add(getInitExprent()); } lst.add(first); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java index 23b1c5e..aa2ef6e 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java @@ -154,6 +154,15 @@ public void setVarDefinitions() { } } } + else if (dstat.getLooptype() == DoStatement.LOOP_FOREACH) { + if (dstat.getInitExprent() != null && dstat.getInitExprent().type == Exprent.EXPRENT_VAR) { + VarExprent var = (VarExprent)dstat.getInitExprent(); + if (var.getIndex() == index.intValue()) { + var.setDefinition(true); + continue; + } + } + } } @@ -269,6 +278,7 @@ private Set initStatement(Statement stat) { if (st.type == DoStatement.TYPE_DO) { DoStatement dost = (DoStatement)st; if (dost.getLooptype() != DoStatement.LOOP_FOR && + dost.getLooptype() != DoStatement.LOOP_FOREACH && dost.getLooptype() != DoStatement.LOOP_DO) { currVars.add(dost.getConditionExprent()); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java index ef07c6b..0f530f9 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java @@ -31,7 +31,7 @@ public class VarProcessor { private VarVersionsProcessor varVersions; private final Map thisVars = new HashMap(); private final Set externalVars = new HashSet(); -private LocalVariableTable lvt; + private LocalVariableTable lvt; public void setVarVersions(RootStatement root) { Map mapOriginalVarIndices = null; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java index eae4cba..cb37d4d 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java @@ -46,6 +46,8 @@ public void setVarVersions(RootStatement root) { FlattenStatementsHelper flattenHelper = new FlattenStatementsHelper(); DirectGraph graph = flattenHelper.buildDirectGraph(root); + org.jetbrains.java.decompiler.util.DotExporter.toDotFile(graph, mt, "setVarVersions"); + mergePhiVersions(ssa, graph); typeProcessor = new VarTypeProcessor(); diff --git a/src/org/jetbrains/java/decompiler/util/DotExporter.java b/src/org/jetbrains/java/decompiler/util/DotExporter.java new file mode 100644 index 0000000..0d41718 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/util/DotExporter.java @@ -0,0 +1,238 @@ +package org.jetbrains.java.decompiler.util; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +import org.jetbrains.java.decompiler.code.cfg.BasicBlock; +import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph; +import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionEdge; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionNode; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionsGraph; +import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet; + +public class DotExporter { + private static final String DOTS_FOLDER = "Z:/dots/"; + private static final boolean DUMP_DOTS = false; + // http://graphs.grevian.org/graph is a nice visualizer for the outputed dots. + + private static String toDotFormat(Statement stat) { + + StringBuffer buffer = new StringBuffer(); + + buffer.append("digraph G {\r\n"); + + for(Statement st : stat.getStats()) { + + String sourceid = st.id + (st.getSuccessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty()?"":"000000"); + + buffer.append(sourceid+" [shape=box,label=\""+sourceid+"\"];\r\n"); + + for(StatEdge edge : st.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL)) { + String destid = edge.getDestination().id + (edge.getDestination().getSuccessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty()?"":"000000"); + + buffer.append(sourceid+"->"+destid+";\r\n"); + + if(!stat.getStats().contains(edge.getDestination())) { + buffer.append(destid+" [label=\""+destid+"\"];\r\n"); + } + } + + for(StatEdge edge : st.getSuccessorEdges(StatEdge.TYPE_EXCEPTION)) { + String destid = edge.getDestination().id + (edge.getDestination().getSuccessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty()?"":"000000"); + + buffer.append(sourceid+" -> "+destid+" [style=dotted];\r\n"); + + if(!stat.getStats().contains(edge.getDestination())) { + buffer.append(destid+" [label=\""+destid+"\"];\r\n"); + } + } + } + + buffer.append("}"); + + return buffer.toString(); + } + + + private static String toDotFormat(ControlFlowGraph graph, boolean showMultipleEdges) { + + StringBuffer buffer = new StringBuffer(); + + buffer.append("digraph G {\r\n"); + + List blocks = graph.getBlocks(); + for(int i=0;i suc = block.getSuccs(); + if(!showMultipleEdges) { + HashSet set = new HashSet(); + set.addAll(suc); + suc = Collections.list(Collections.enumeration(set)); + } + for(int j=0;j"+((BasicBlock)suc.get(j)).id+";\r\n"); + } + + + suc = block.getSuccExceptions(); + if(!showMultipleEdges) { + HashSet set = new HashSet(); + set.addAll(suc); + suc = Collections.list(Collections.enumeration(set)); + } + for(int j=0;j "+((BasicBlock)suc.get(j)).id+" [style=dotted];\r\n"); + } + } + + buffer.append("}"); + + return buffer.toString(); + } + + private static String toDotFormat(VarVersionsGraph graph) { + + StringBuffer buffer = new StringBuffer(); + + buffer.append("digraph G {\r\n"); + + List blocks = graph.nodes; + for(int i=0;i"+(dest.var*1000+dest.version)+(edge.type==VarVersionEdge.EDGE_PHANTOM?" [style=dotted]":"")+";\r\n"); + } + } + + buffer.append("}"); + + return buffer.toString(); + } + + private static String toDotFormat(DirectGraph graph, Map vars) { + + StringBuffer buffer = new StringBuffer(); + + buffer.append("digraph G {\r\n"); + + List blocks = graph.nodes; + for(int i=0;i>> lst = map.entryList(); + if (lst != null) { + for (Entry> entry : lst) { + label.append("\\n").append(entry.getKey()); + Set set = entry.getValue().toPlainSet(); + label.append("=").append(set.toString()); + } + } + } + + buffer.append(directBlockIdToDot(block.id)+" [shape=box,label=\""+label+"\"];\r\n"); + + for(DirectNode dest: block.succs) { + buffer.append(directBlockIdToDot(block.id)+"->"+directBlockIdToDot(dest.id)+";\r\n"); + } + } + + buffer.append("}"); + + return buffer.toString(); + } + + private static String directBlockIdToDot(String id) { + id = id.replaceAll("_try", "999"); + id = id.replaceAll("_tail", "888"); + + id = id.replaceAll("_init", "111"); + id = id.replaceAll("_cond", "222"); + id = id.replaceAll("_inc", "333"); + return id; + } + + private static File getFile(StructMethod mt, String suffix) { + File root = new File(DOTS_FOLDER + mt.getClassStruct().qualifiedName); + if (!root.isDirectory()) + root.mkdirs(); + return new File(root, + mt.getName().replace('<', '.').replace('>', '_') + + mt.getDescriptor().replace('/', '.') + + '_' + suffix + ".dot"); + } + + public static void toDotFile(DirectGraph dgraph, StructMethod mt, String suffix) { + toDotFile(dgraph, mt, suffix, null); + } + public static void toDotFile(DirectGraph dgraph, StructMethod mt, String suffix, Map vars) { + if (!DUMP_DOTS) + return; + try{ + BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(getFile(mt, suffix))); + out.write(toDotFormat(dgraph, vars).getBytes()); + out.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void toDotFile(Statement stat, StructMethod mt, String suffix) { + if (!DUMP_DOTS) + return; + try{ + BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(getFile(mt, suffix))); + out.write(toDotFormat(stat).getBytes()); + out.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void toDotFile(VarVersionsGraph graph, StructMethod mt, String suffix) { + if (!DUMP_DOTS) + return; + try{ + BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(getFile(mt, suffix))); + out.write(toDotFormat(graph).getBytes()); + out.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void toDotFile(ControlFlowGraph graph, StructMethod mt, String suffix, boolean showMultipleEdges) { + if (!DUMP_DOTS) + return; + try{ + BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(getFile(mt, suffix))); + out.write(toDotFormat(graph, showMultipleEdges).getBytes()); + out.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file From 1326398474ddba2211b73412ca7be68ba9c32342 Mon Sep 17 00:00:00 2001 From: cpw Date: Thu, 13 Aug 2015 17:06:24 -0700 Subject: [PATCH 10/73] Fix permissions on gradlew. It's writeable on linux. --- gradlew | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 gradlew diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 From 52c5082cd65ed6436412b97c742edf08878890f8 Mon Sep 17 00:00:00 2001 From: cpw Date: Thu, 13 Aug 2015 21:45:39 -0700 Subject: [PATCH 11/73] Partially revert "TryCatch statements will now use the correct variable index for their exception variable." (rolled back catch changes) This reverts commit 5f699acc70d8e40f758737ae88aeddecc07edc8c. --- .../modules/decompiler/stats/CatchAllStatement.java | 11 +---------- .../modules/decompiler/stats/CatchStatement.java | 10 +--------- testData/results/TestClassSimpleBytecodeMapping.dec | 4 ++-- testData/results/TestTryCatchFinally.dec | 12 ++++++------ 4 files changed, 10 insertions(+), 27 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java index d5b432c..812ed10 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java @@ -16,7 +16,6 @@ package org.jetbrains.java.decompiler.modules.decompiler.stats; import org.jetbrains.java.decompiler.code.CodeConstants; -import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.TextBuffer; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; @@ -69,15 +68,7 @@ private CatchAllStatement(Statement head, Statement handler) { } } - int varIndex = -1; - BasicBlock block = handler.getBasichead().getBlock(); - if (!block.getSeq().isEmpty() && block.getInstruction(0).opcode == CodeConstants.opc_astore) { - varIndex = block.getInstruction(0).getOperand(0); - } - else { - varIndex = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER); - } - vars.add(new VarExprent(varIndex, + vars.add(new VarExprent(DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER), new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Throwable"), (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR))); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java index fda5ed3..8d56042 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java @@ -60,15 +60,7 @@ private CatchStatement(Statement head, Statement next, HashSet setHan stats.addWithKey(stat, stat.id); exctstrings.add(new ArrayList(edge.getExceptions())); - int varIndex = -1; - BasicBlock block = stat.getBasichead().getBlock(); - if (!block.getSeq().isEmpty() && block.getInstruction(0).opcode == CodeConstants.opc_astore) { - varIndex = block.getInstruction(0).getOperand(0); - } - else { - varIndex = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER); - } - vars.add(new VarExprent(varIndex, + vars.add(new VarExprent(DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER), new VarType(CodeConstants.TYPE_OBJECT, 0, edge.getExceptions().get(0)), // FIXME: for now simply the first type. Should get the first common superclass when possible. (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR))); diff --git a/testData/results/TestClassSimpleBytecodeMapping.dec b/testData/results/TestClassSimpleBytecodeMapping.dec index 4a2f333..1a2532f 100644 --- a/testData/results/TestClassSimpleBytecodeMapping.dec +++ b/testData/results/TestClassSimpleBytecodeMapping.dec @@ -21,8 +21,8 @@ public class TestClassSimpleBytecodeMapping { public void test2(String var1) { try { Integer.parseInt(var1);// 34 - } catch (Exception var2) {// 35 - System.out.println(var2);// 36 + } catch (Exception var6) {// 35 + System.out.println(var6);// 36 } finally { System.out.println("Finally");// 38 } diff --git a/testData/results/TestTryCatchFinally.dec b/testData/results/TestTryCatchFinally.dec index fbe5782..772a76e 100644 --- a/testData/results/TestTryCatchFinally.dec +++ b/testData/results/TestTryCatchFinally.dec @@ -4,10 +4,10 @@ public class TestTryCatchFinally { public void test1(String var1) { try { System.out.println("sout1");// 24 - } catch (Exception var2) { + } catch (Exception var9) { try { System.out.println("sout2");// 27 - } catch (Exception var3) {// 28 + } catch (Exception var8) {// 28 ; } } finally { @@ -28,10 +28,10 @@ public class TestTryCatchFinally { public int test(String var1) { try { - int var5 = Integer.parseInt(var1);// 51 - return var5; - } catch (Exception var2) {// 52 - System.out.println("Error" + var2);// 53 + int var2 = Integer.parseInt(var1);// 51 + return var2; + } catch (Exception var6) {// 52 + System.out.println("Error" + var6);// 53 } finally { System.out.println("Finally");// 55 } From 1d9d7cdb8476a39a10b588465f749347b9d1c713 Mon Sep 17 00:00:00 2001 From: cpw Date: Thu, 13 Aug 2015 22:57:55 -0700 Subject: [PATCH 12/73] Added a test for variables that seem to come into scope if we remove VarVersionsProcessor.simpleMerge --- testData/classes/pkg/TestLVT.class | Bin 1672 -> 1860 bytes testData/results/TestLVT.dec | 11 +++++++++-- testData/src/pkg/TestLVT.java | 9 +++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/testData/classes/pkg/TestLVT.class b/testData/classes/pkg/TestLVT.class index d8a5829d4f6504567ecf46bf65167047e41cd291..1cf6286f2a679368b301d408c928d93ed4eecf48 100644 GIT binary patch delta 224 zcmeC+J;KLz>ff$?3=9lL3~3v=v{}sAb5lz)@>3GI7_1qhc^K>%Vi*}%G+Z>p*z9-C z1c6y>_8%b(Mh3=2Aa()bq{+rC>Ws{rqggbV>e(0=8Q6hFmNKvcAp?Uvp9jC!CI%CA z&E#ziQ6dbn9wH3!2!V}2B}@!lKvE8@O%}+LV_;#BXW(T}U=UD=38B)MfsQ}ff$?3=9lL4AC38v{@$Sv8XaKZJxrS!NkhIz{C&*0Mu#;EC2ui diff --git a/testData/results/TestLVT.dec b/testData/results/TestLVT.dec index b1b7e2e..d161f07 100644 --- a/testData/results/TestLVT.dec +++ b/testData/results/TestLVT.dec @@ -23,7 +23,14 @@ public class TestLVT { String spam = scope1a + scope2 + scope2a + i + noise; System.out.println(spam); } - } -} + public void methoda() { + double a = 0.0D; + double b = 1.0D; + System.out.println(a + b); + a = 0.1D; + b = 1.1D; + System.out.println(a + b); + } +} diff --git a/testData/src/pkg/TestLVT.java b/testData/src/pkg/TestLVT.java index 05b5228..9a46272 100644 --- a/testData/src/pkg/TestLVT.java +++ b/testData/src/pkg/TestLVT.java @@ -24,4 +24,13 @@ public static void method(String a1, String a2) { System.out.println(spam); } } + + public void methoda() { + double a = 0D; + double b = 1D; + System.out.println(a+b); + a = 0.1D; + b = 1.1D; + System.out.println(a+b); + } } From c45318da6da9d2d073e1979350e67042d03d4b81 Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Fri, 14 Aug 2015 15:56:08 -0700 Subject: [PATCH 13/73] Hook in LVT to VarExprent and print generic types. --- .../decompiler/main/rels/ClassWrapper.java | 13 ++++++++-- .../modules/decompiler/ExprProcessor.java | 7 ++++++ .../decompiler/exps/InvocationExprent.java | 3 +++ .../modules/decompiler/exps/VarExprent.java | 24 +++++++++++++++---- .../modules/decompiler/vars/LVTVariable.java | 14 ++++++++--- .../decompiler/vars/LocalVariableTable.java | 11 ++++----- .../decompiler/vars/VarDefinitionHelper.java | 10 ++++++++ .../modules/decompiler/vars/VarProcessor.java | 4 ++++ .../StructLocalVariableTableAttribute.java | 4 ++-- 9 files changed, 73 insertions(+), 17 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java b/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java index 01b9292..c302cfc 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java +++ b/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java @@ -81,6 +81,16 @@ public void init() throws IOException { RootStatement root = null; + // if debug information present and should be used + if (DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_VAR_NAMES)) { + StructLocalVariableTableAttribute attr = (StructLocalVariableTableAttribute)mt.getAttributes().getWithKey( + StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE); + + if (attr != null) { + varProc.setLVT(attr.getLVT()); + } + } + boolean isError = false; try { @@ -169,12 +179,11 @@ public void init() throws IOException { StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE); if (attr != null) { - varProc.setLVT(attr.getLVT()); varProc.setDebugVarNames(attr.getMapVarNames()); } } - MethodProcessorRunnable.printMethod(root, mt.getClassStruct().qualifiedName+"."+mt.getName()+mt.getDescriptor(),varProc); + //MethodProcessorRunnable.printMethod(root, mt.getClassStruct().qualifiedName+"."+mt.getName()+mt.getDescriptor(),varProc); DecompilerContext.getLogger().endMethod(); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java index f33e438..0425fbc 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java @@ -38,6 +38,8 @@ import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.struct.gen.generics.GenericMain; +import org.jetbrains.java.decompiler.struct.gen.generics.GenericType; import java.util.*; @@ -725,6 +727,11 @@ else if (tp == CodeConstants.TYPE_VOID) { return "void"; } else if (tp == CodeConstants.TYPE_OBJECT) { + //if (type.signatureDebugGens != null) { + // GenericType gen = new GenericType(type.signatureDebugGens); + // return GenericMain.getGenericCastTypeName(gen); + //} + String ret = buildJavaClassName(type.value); if (getShort) { ret = DecompilerContext.getImportCollector().getShortName(ret); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java index 7cf83be..9d16de2 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java @@ -241,6 +241,9 @@ public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { if (super_qualifier != null) { TextUtil.writeQualifiedSuper(buf, super_qualifier); } + //else if (getExprType().equals(VarType.VARTYPE_OBJECT) && instance instanceof FunctionExprent && ((FunctionExprent)instance).getFuncType() == FunctionExprent.FUNCTION_CAST) { + // buf.append(((FunctionExprent)instance).getLstOperands().get(0).toJava(indent, tracer)); + //} // THis in theory removes casts that are not needed... ignore it for now. else if (instance != null) { TextBuffer res = instance.toJava(indent, tracer); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java index 969a03e..6c973af 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java @@ -22,13 +22,15 @@ import org.jetbrains.java.decompiler.main.TextBuffer; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.vars.LVTVariable; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.struct.gen.generics.GenericMain; +import org.jetbrains.java.decompiler.struct.gen.generics.GenericType; import org.jetbrains.java.decompiler.struct.match.MatchEngine; import org.jetbrains.java.decompiler.struct.match.MatchNode; -import org.jetbrains.java.decompiler.struct.match.IMatchable.MatchProperties; import org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue; import org.jetbrains.java.decompiler.util.InterpreterUtil; @@ -48,6 +50,7 @@ public class VarExprent extends Exprent { private int version = 0; private boolean classDef = false; private boolean stack = false; + private LVTVariable lvt = null; public VarExprent(int index, VarType varType, VarProcessor processor) { super(EXPRENT_VAR); @@ -94,7 +97,10 @@ public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { } else { String name = null; - if (processor != null) { + if (lvt != null) { + name = lvt.name; + } + else if (processor != null) { name = processor.getVarName(new VarVersionPair(index, version)); } @@ -102,7 +108,12 @@ public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { if (processor != null && processor.getVarFinal(new VarVersionPair(index, version)) == VarTypeProcessor.VAR_EXPLICIT_FINAL) { buffer.append("final "); } - buffer.append(ExprProcessor.getCastTypeName(getVarType())).append(" "); + if (lvt != null && lvt.getSig() != null) { + buffer.append(GenericMain.getGenericCastTypeName(new GenericType(lvt.getSig()))).append(" "); + } + else { + buffer.append(ExprProcessor.getCastTypeName(getVarType())).append(" "); + } } buffer.append(name == null ? ("var" + index + (version == 0 ? "" : "_" + version)) : name); } @@ -136,6 +147,7 @@ public void setIndex(int index) { public VarType getVarType() { VarType vt = null; + if (processor != null) { vt = processor.getVarType(new VarVersionPair(index, version)); } @@ -217,9 +229,13 @@ public boolean match(MatchNode matchNode, MatchEngine engine) { return true; } + public void setLVT(LVTVariable lvt) { + this.lvt = lvt; + } + @Override public String toString() { - return "var_" + index + "_" + version; + return lvt != null ? lvt.name : "var_" + index + "_" + version; } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LVTVariable.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LVTVariable.java index 13cfa70..dc7783c 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LVTVariable.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LVTVariable.java @@ -68,7 +68,15 @@ public int compareTo(LVTVariable o) { return 0; } @Override - public String toString() { - return "\'("+index+","+start+")"+desc+(sig!=null ? "<"+sig+"> ":" ")+name+"\'"; - } + public String toString() { + return "\'("+index+","+start+")"+desc+(sig!=null ? "<"+sig+"> ":" ")+name+"\'"; + } + + public String getDesc() { + return desc; + } + + public String getSig() { + return sig; + } } \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java index e5fbec8..270def3 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java @@ -9,7 +9,7 @@ public class LocalVariableTable { private Map> startpoints; private ArrayList allLVT; -private Map> mapLVT; + private Map> mapLVT; public LocalVariableTable(int len) { startpoints = new HashMap>(len); @@ -35,15 +35,14 @@ public void mergeLVTs(LocalVariableTable otherLVT) { mapLVT = null; // Invalidate the cache and rebuild it. } - public LVTVariable find(Integer index, List offsets) { + public LVTVariable find(int index, List offsets) { for (Integer offset : offsets) { Set lvs = startpoints.get(offset); if (lvs == null || lvs.isEmpty()) continue; - int idx = index.intValue(); for (LVTVariable lv : lvs) { - if (lv.index == idx) + if (lv.index == index) return lv; } } @@ -75,7 +74,7 @@ private void buildNameMap() { } } -public List getCandidates(int index) { + public List getCandidates(int index) { return mapLVT.get(index); -} + } } \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java index aa2ef6e..53201ed 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java @@ -185,6 +185,16 @@ else if (first.getExprents() == null) { // search for the first assignement to var [index] int addindex = 0; for (Exprent expr : lst) { + LVTVariable lvt = varproc.findLVT(index, first.getBasichead().getBlock().getInstrOldOffsets()); + if (lvt != null) { + for (Exprent exp : expr.getAllExprents(true)) { + if (exp.type == Exprent.EXPRENT_VAR && ((VarExprent)exp).getIndex() == index) { + ((VarExprent)exp).setLVT(lvt); + break; + } + } + } + if (setDefinition(expr, index)) { defset = true; break; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java index 0f530f9..0a619d0 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java @@ -160,6 +160,10 @@ public LocalVariableTable getLVT() { return this.lvt; } + public LVTVariable findLVT(int index, List instructionOffsets) { + return this.lvt == null ? null : lvt.find(index, instructionOffsets); + } + public int getRemapped(int index) { VarVersionPair res = varVersions.getMapOriginalVarIndices().get(index); if (res == null) return index; diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java index a654359..771ec7a 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java @@ -69,7 +69,7 @@ public Map> getMapVarNames() { return lvt == null ? EMPTY_LVT : lvt.getMapVarNames(); } -public LocalVariableTable getLVT() { + public LocalVariableTable getLVT() { return lvt; -} + } } From fff1261eeb1e19a8f57fea9b1a81bce7894f9aef Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Tue, 25 Aug 2015 12:03:36 -0700 Subject: [PATCH 14/73] Make Loop Enhancement retain all bytecode data. And support iterator/listIterator. --- .../modules/decompiler/MergeHelper.java | 147 ++++++++++++++---- 1 file changed, 115 insertions(+), 32 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java index d994c26..3172b5e 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java @@ -118,6 +118,10 @@ && isDirectPath(stat, elseedge.getDestination()))) { if (ifedge.getType() == StatEdge.TYPE_BREAK) { ifexpr.negateIf(); } + if (stat.getConditionExprent() != null) { + ifexpr.getCondition().addBytecodeOffsets(stat.getConditionExprent().bytecode); + } + ifexpr.getCondition().addBytecodeOffsets(lastif.getHeadexprent().bytecode); stat.setConditionExprent(ifexpr.getCondition()); lastif.getFirst().removeSuccessor(ifedge); lastif.removeSuccessor(elseedge); @@ -175,6 +179,10 @@ private static boolean matchWhile(DoStatement stat) { // negate condition (while header) IfExprent ifexpr = (IfExprent)firstif.getHeadexprent().copy(); ifexpr.negateIf(); + if (stat.getConditionExprent() != null) { + ifexpr.getCondition().addBytecodeOffsets(stat.getConditionExprent().bytecode); + } + ifexpr.getCondition().addBytecodeOffsets(firstif.getHeadexprent().bytecode); stat.setConditionExprent(ifexpr.getCondition()); // remove edges @@ -213,7 +221,12 @@ private static boolean matchWhile(DoStatement stat) { stat.setLooptype(DoStatement.LOOP_WHILE); // no need to negate the while condition - stat.setConditionExprent(((IfExprent)firstif.getHeadexprent().copy()).getCondition()); + IfExprent ifexpr = (IfExprent)firstif.getHeadexprent().copy(); + if (stat.getConditionExprent() != null) { + ifexpr.getCondition().addBytecodeOffsets(stat.getConditionExprent().bytecode); + } + ifexpr.getCondition().addBytecodeOffsets(firstif.getHeadexprent().bytecode); + stat.setConditionExprent(ifexpr.getCondition()); // remove edges StatEdge ifedge = firstif.getIfEdge(); @@ -369,9 +382,17 @@ private static boolean matchFor(DoStatement stat) { stat.setLooptype(DoStatement.LOOP_FOR); if (hasinit) { - stat.setInitExprent(preData.getExprents().remove(preData.getExprents().size() - 1)); + Exprent exp = preData.getExprents().remove(preData.getExprents().size() - 1); + if (stat.getInitExprent() != null) { + exp.addBytecodeOffsets(stat.getInitExprent().bytecode); + } + stat.setInitExprent(exp); } - stat.setIncExprent(lastData.getExprents().remove(lastData.getExprents().size() - 1)); + Exprent exp = lastData.getExprents().remove(lastData.getExprents().size() - 1); + if (stat.getIncExprent() != null) { + exp.addBytecodeOffsets(stat.getIncExprent().bytecode); + } + stat.setIncExprent(exp); } cleanEmptyStatements(stat, lastData); @@ -425,20 +446,29 @@ private static boolean matchForEach(DoStatement stat) { if (stat.getLooptype() == DoStatement.LOOP_WHILE && initDoExprent != null && firstDoExprent != null) { if (initDoExprent.type == Exprent.EXPRENT_ASSIGNMENT && - isInvoke(((AssignmentExprent)initDoExprent).getRight(), null, "iterator", "()Ljava/util/Iterator;")) { + isIteratorCall(((AssignmentExprent)initDoExprent).getRight())) { - if (!isInvoke(drillNots(stat.getConditionExprent()), "java/util/Iterator", "hasNext", "()Z") || + if (!isHasNextCall(drillNots(stat.getConditionExprent())) || firstDoExprent.type != Exprent.EXPRENT_ASSIGNMENT) { return false; } AssignmentExprent ass = (AssignmentExprent)firstDoExprent; - if (!isInvoke(ass.getRight(), "java/util/Iterator", "next", "()Ljava/lang/Object;") || - ass.getLeft().type != Exprent.EXPRENT_VAR) { + if (!isNextCall(ass.getRight()) || ass.getLeft().type != Exprent.EXPRENT_VAR) { return false; } InvocationExprent holder = (InvocationExprent)((AssignmentExprent)initDoExprent).getRight(); + + holder.getInstance().addBytecodeOffsets(initDoExprent.bytecode); + ass.getLeft().addBytecodeOffsets(firstDoExprent.bytecode); + if (stat.getIncExprent() != null) { + holder.getInstance().addBytecodeOffsets(stat.getIncExprent().bytecode); + } + if (stat.getInitExprent() != null) { + ass.getLeft().addBytecodeOffsets(stat.getInitExprent().bytecode); + } + stat.setLooptype(DoStatement.LOOP_FOREACH); stat.setInitExprent(ass.getLeft()); stat.setIncExprent(holder.getInstance()); @@ -448,24 +478,48 @@ private static boolean matchForEach(DoStatement stat) { } else if (stat.getLooptype() == DoStatement.LOOP_FOR) { if (isType(stat.getInitExprent(), Exprent.EXPRENT_ASSIGNMENT) && - isInvoke(((AssignmentExprent)stat.getInitExprent()).getRight(), null, "iterator", "()Ljava/util/Iterator;") && + isIteratorCall(((AssignmentExprent)stat.getInitExprent()).getRight()) && isType(stat.getConditionExprent(), Exprent.EXPRENT_FUNCTION)) { - if (!isInvoke(drillNots(stat.getConditionExprent()), "java/util/Iterator", "hasNext", "()Z") || + Exprent exp = drillNots(stat.getConditionExprent()); + if (!isHasNextCall(exp) || !isType(stat.getIncExprent(), Exprent.EXPRENT_ASSIGNMENT)) { return false; } AssignmentExprent ass = (AssignmentExprent)stat.getIncExprent(); - if (!isInvoke(ass.getRight(), "java/util/Iterator", "next", "()Ljava/lang/Object;") || - ass.getLeft().type != Exprent.EXPRENT_VAR) { - return false; + if (!isNextCall(ass.getRight()) || ass.getLeft().type != Exprent.EXPRENT_VAR) { + + if (firstDoExprent == null || !isNextCall(firstDoExprent.getRight()) || + firstDoExprent.getLeft().type != Exprent.EXPRENT_VAR) { + return false; + } + + //Move the inc exprent back to the end of the body and remove the .next call + Statement last = getLastDirectData(stat.getFirst()); + InvocationExprent holder = (InvocationExprent)getUncast(((AssignmentExprent)stat.getInitExprent()).getRight()); + + firstData.getExprents().remove(firstDoExprent); + last.getExprents().add(stat.getIncExprent()); + + firstDoExprent.getLeft().addBytecodeOffsets(stat.getInitExprent().bytecode); + firstDoExprent.getLeft().addBytecodeOffsets(stat.getIncExprent().bytecode); + firstDoExprent.getLeft().addBytecodeOffsets(firstDoExprent.bytecode); + + stat.setLooptype(DoStatement.LOOP_FOREACH); + stat.setInitExprent(firstDoExprent.getLeft()); + stat.setIncExprent(holder.getInstance()); } + else { + InvocationExprent holder = (InvocationExprent)getUncast(((AssignmentExprent)stat.getInitExprent()).getRight()); - InvocationExprent holder = (InvocationExprent)((AssignmentExprent)stat.getInitExprent()).getRight(); - stat.setLooptype(DoStatement.LOOP_FOREACH); - stat.setInitExprent(ass.getLeft()); - stat.setIncExprent(holder.getInstance()); + holder.getInstance().addBytecodeOffsets(stat.getInitExprent().bytecode); + ass.getLeft().addBytecodeOffsets(stat.getIncExprent().bytecode); + + stat.setLooptype(DoStatement.LOOP_FOREACH); + stat.setInitExprent(ass.getLeft()); + stat.setIncExprent(holder.getInstance()); + } } else if (initDoExprent != null && initDoExprent.getRight().type == Exprent.EXPRENT_FUNCTION) { if (firstDoExprent == null || @@ -496,23 +550,31 @@ else if (initDoExprent != null && initDoExprent.getRight().type == Exprent.EXPRE return false; } + funcRight.getLstOperands().get(0).addBytecodeOffsets(initDoExprent.bytecode); + funcRight.getLstOperands().get(0).addBytecodeOffsets(stat.getIncExprent().bytecode); + firstDoExprent.getLeft().addBytecodeOffsets(firstDoExprent.bytecode); + firstDoExprent.getLeft().addBytecodeOffsets(stat.getInitExprent().bytecode); + stat.setLooptype(DoStatement.LOOP_FOREACH); stat.setInitExprent(firstDoExprent.getLeft()); stat.setIncExprent(funcRight.getLstOperands().get(0)); preData.getExprents().remove(initDoExprent); firstData.getExprents().remove(firstDoExprent); + if (initCopyExprent != null && initCopyExprent.getLeft().type == Exprent.EXPRENT_VAR) { VarExprent copy = (VarExprent)initCopyExprent.getLeft(); if (copy.getIndex() == array.getIndex() && copy.getVersion() == array.getVersion()) { preData.getExprents().remove(initCopyExprent); + initCopyExprent.getRight().addBytecodeOffsets(initCopyExprent.bytecode); + initCopyExprent.getRight().addBytecodeOffsets(stat.getIncExprent().bytecode); stat.setIncExprent(initCopyExprent.getRight()); } } } } - cleanEmptyStatements(stat, firstData); + //cleanEmptyStatements(stat, firstData); //TODO: Look into this and see what it does... return true; } @@ -520,8 +582,8 @@ else if (initDoExprent != null && initDoExprent.getRight().type == Exprent.EXPRE private static boolean isType(Exprent exp, int type) { //This is just a helper macro, Wish java had real macros. return exp != null && exp.type == type; } - private static boolean isInvoke(Exprent exp, String cls, String method, String desc) { + exp = getUncast(exp); if (!isType(exp, Exprent.EXPRENT_INVOCATION)) { return false; } @@ -591,15 +653,13 @@ private static Statement getLastDirectData(Statement stat) { return stat; } - switch (stat.type) { - case Statement.TYPE_SEQUENCE: - for (int i = stat.getStats().size() - 1; i >= 0; i--) { - Statement tmp = getLastDirectData(stat.getStats().get(i)); - if (tmp == null || !tmp.getExprents().isEmpty()) { - return tmp; - } - } + for (int i = stat.getStats().size() - 1; i >= 0; i--) { + Statement tmp = getLastDirectData(stat.getStats().get(i)); + if (tmp == null || !tmp.getExprents().isEmpty()) { + return tmp; + } } + return null; } @@ -608,14 +668,37 @@ private static Statement getFirstDirectData(Statement stat) { return stat; } - if (stat.type == Statement.TYPE_SEQUENCE) { - for (Statement tmp : stat.getStats()) { - Statement ret = getFirstDirectData(tmp); - if (ret != null) { - return ret; - } + for (Statement tmp : stat.getStats()) { + Statement ret = getFirstDirectData(tmp); + if (ret != null) { + return ret; } } return null; } + + private static Exprent getUncast(Exprent exp) { + if (exp.type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent func = (FunctionExprent)exp; + if (func.getFuncType() == FunctionExprent.FUNCTION_CAST) { + return getUncast(func.getLstOperands().get(0)); + } + } + return exp; + } + + private static boolean isIteratorCall(Exprent exp) { + return isInvoke(exp, null, "iterator", "()Ljava/util/Iterator;" ) || + isInvoke(exp, null, "listIterator", "()Ljava/util/ListIterator;"); + } + + private static boolean isHasNextCall(Exprent exp) { + return isInvoke(exp, "java/util/Iterator", "hasNext", "()Z") || + isInvoke(exp, "java/util/ListIterator", "hasNext", "()Z"); + } + + private static boolean isNextCall(Exprent exp) { + return isInvoke(exp, "java/util/Iterator", "next", "()Ljava/lang/Object;") || + isInvoke(exp, "java/util/ListIterator", "next", "()Ljava/lang/Object;"); + } } From 5d42ed490c525d168d6d2f9d727610aa6262237c Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Tue, 25 Aug 2015 12:05:24 -0700 Subject: [PATCH 15/73] Add step to VarDefintionHelper that splits variables out as best it can. For now ForEach loops are forced to seperate variables. Also attempt to attach LVT information EVERY VarExprent in the method not just in head. --- .../main/rels/MethodProcessorRunnable.java | 2 +- .../modules/decompiler/exps/Exprent.java | 1 - .../sforms/FlattenStatementsHelper.java | 4 +- .../decompiler/vars/LocalVariableTable.java | 64 ++++- .../decompiler/vars/VarDefinitionHelper.java | 248 +++++++++++++++++- .../modules/decompiler/vars/VarProcessor.java | 11 +- 6 files changed, 304 insertions(+), 26 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java index 9542a80..54a77e1 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java @@ -243,7 +243,7 @@ public static void printMethod(RootStatement root, String name, VarProcessor var System.out.println("}"); } - private static void getOffset(Statement st, BitSet values) { + public static void getOffset(Statement st, BitSet values) { if (st instanceof DummyExitStatement && ((DummyExitStatement)st).bytecode != null) values.or(((DummyExitStatement)st).bytecode); if (st.getExprents() != null) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java index db839fe..ca9c1ed 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java @@ -17,7 +17,6 @@ import java.util.ArrayList; import java.util.BitSet; -import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java index d88480e..abc0f67 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java @@ -227,7 +227,9 @@ public StatementStackEntry(Statement statement, LinkedList stackFina graph.nodes.putWithKey(nodeinit, nodeinit.id); DirectNode nodecond = new DirectNode(DirectNode.NODE_CONDITION, stat, stat.id + "_cond"); - nodecond.exprents = (stat.type == DoStatement.LOOP_FOREACH ? null : dostat.getConditionExprentList()); + if (looptype != DoStatement.LOOP_FOREACH) { + nodecond.exprents = dostat.getConditionExprentList(); + } graph.nodes.putWithKey(nodecond, nodecond.id); DirectNode nodeinc = new DirectNode(DirectNode.NODE_INCREMENT, stat, stat.id + "_inc"); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java index 270def3..cd56bac 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java @@ -1,11 +1,16 @@ package org.jetbrains.java.decompiler.modules.decompiler.vars; import java.util.ArrayList; +import java.util.BitSet; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; +import org.jetbrains.java.decompiler.main.rels.MethodProcessorRunnable; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; + public class LocalVariableTable { private Map> startpoints; private ArrayList allLVT; @@ -35,15 +40,20 @@ public void mergeLVTs(LocalVariableTable otherLVT) { mapLVT = null; // Invalidate the cache and rebuild it. } - public LVTVariable find(int index, List offsets) { - for (Integer offset : offsets) { - Set lvs = startpoints.get(offset); - if (lvs == null || lvs.isEmpty()) - continue; + public LVTVariable find(int index, Statement stat) { + BitSet values = new BitSet(); + MethodProcessorRunnable.getOffset(stat, values); + int start = values.nextSetBit(0); + int end = values.length()-1; + //System.out.println(indent + stat.getClass().getSimpleName() + " (" + start +", " + end + ")"); - for (LVTVariable lv : lvs) { - if (lv.index == index) - return lv; + Map> map = getMapVarNames(); + if (!map.containsKey(index)) { + return null; + } + for (LVTVariable lvt : map.get(index)) { + if (lvt.start >= start && lvt.end <= end) { + return lvt; } } return null; @@ -75,6 +85,42 @@ private void buildNameMap() { } public List getCandidates(int index) { - return mapLVT.get(index); + return getMapVarNames().get(index); + } + + public List getVars(int index, int start, int end) { + if (!getMapVarNames().containsKey(index)) { + return null; + } + + List ret = new ArrayList(); + for (LVTVariable lvt : getMapVarNames().get(index)) { + if (lvt.start >= start && lvt.end <= end) { + ret.add(lvt); + } + } + + return ret; + } + + public Map getVars(Statement stat) { + BitSet values = new BitSet(); + MethodProcessorRunnable.getOffset(stat, values); + int start = values.nextSetBit(0); + int end = values.length()-1; + //System.out.println(indent + stat.getClass().getSimpleName() + " (" + start +", " + end + ")"); + + Map ret = new HashMap(); + for (Entry> entry : getMapVarNames().entrySet()) { + for (LVTVariable lvt : entry.getValue()) { + if (lvt.start >= start && lvt.end <= end) { + if (ret.containsKey(entry.getKey())) { + System.out.println("DUPLICATE INDEX WHAT THE FUCK: " + entry.getKey()); + } + ret.put(entry.getKey(), lvt); + } + } + } + return ret; } } \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java index 53201ed..1e1a4a7 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java @@ -17,7 +17,9 @@ import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector; +import org.jetbrains.java.decompiler.main.rels.MethodProcessorRunnable; import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; @@ -43,6 +45,8 @@ public class VarDefinitionHelper { private final VarProcessor varproc; + private final CounterContainer counters = DecompilerContext.getCounterContainer(); + public VarDefinitionHelper(Statement root, StructMethod mt, VarProcessor varproc) { mapVarDefStatements = new HashMap(); @@ -91,6 +95,8 @@ public VarDefinitionHelper(Statement root, StructMethod mt, VarProcessor varproc vc.addName("this"); } + splitVaribles(root, ""); + // catch variables are implicitly defined LinkedList stack = new LinkedList(); stack.add(root); @@ -120,7 +126,6 @@ else if (st.type == Statement.TYPE_TRYCATCH) { initStatement(root); } - public void setVarDefinitions() { VarNamesCollector vc = DecompilerContext.getVarNamesCollector(); @@ -129,6 +134,8 @@ public void setVarDefinitions() { Statement stat = en.getValue(); Integer index = en.getKey(); + setupLVTs(stat); + if (implDefVars.contains(index)) { // already implicitly defined continue; @@ -165,7 +172,6 @@ else if (dstat.getLooptype() == DoStatement.LOOP_FOREACH) { } } - Statement first = findFirstBlock(stat, index); List lst; @@ -179,21 +185,11 @@ else if (first.getExprents() == null) { lst = first.getExprents(); } - boolean defset = false; // search for the first assignement to var [index] int addindex = 0; for (Exprent expr : lst) { - LVTVariable lvt = varproc.findLVT(index, first.getBasichead().getBlock().getInstrOldOffsets()); - if (lvt != null) { - for (Exprent exp : expr.getAllExprents(true)) { - if (exp.type == Exprent.EXPRENT_VAR && ((VarExprent)exp).getIndex() == index) { - ((VarExprent)exp).setLVT(lvt); - break; - } - } - } if (setDefinition(expr, index)) { defset = true; @@ -218,6 +214,21 @@ else if (first.getExprents() == null) { VarExprent var = new VarExprent(index.intValue(), varproc.getVarType(new VarVersionPair(index.intValue(), 0)), varproc); var.setDefinition(true); + if (varproc.getLVT() != null) { + BitSet values = new BitSet(); + MethodProcessorRunnable.getOffset(stat, values); + int start = values.nextSetBit(0); + int end = values.length()-1; + List vars = varproc.getLVT().getVars(index, start, end); + if (vars != null) { + if (vars.size() == 1) { + var.setLVT(vars.get(0)); + } + // ToDo: If this is >1 then we need to decrease the scope of these variables. + //if this is = 0 and we have lvts for this... then we need to expand the scope... + } + } + lst.add(addindex, var); } } @@ -372,4 +383,217 @@ private static boolean setDefinition(Exprent expr, Integer index) { } return false; } + + //We merge variables together earlier to make ++ and -- work, we need to now + // split them using the LVT data and full method structure into separate versions. + // this also allows us to re-index/version variables that were not done + // before such as exceptions. + // Return value of referenced variables + // The boolean is 'isAssignment', true if first reference is assignment, false if just reference. + private Map splitVaribles(Statement stat, String indent) { + Map vars = new HashMap(); + + //BitSet values = new BitSet(); + //MethodProcessorRunnable.getOffset(stat, values); + //int start = values.nextSetBit(0); + //int end = values.length()-1; + //System.out.println(indent + stat.getClass().getSimpleName() + " (" + start +", " + end + ")"); + + if (stat.type == Statement.TYPE_DO) { + DoStatement dost = (DoStatement)stat; + if (dost.getLooptype() == DoStatement.LOOP_FOREACH) { + vars.put(new VarVersionPair((VarExprent)dost.getInitExprent()), true); + splitExprent(dost.getIncExprent(), vars, indent); + } + else if (dost.getLooptype() == DoStatement.LOOP_FOR) { + splitExprent(dost.getInitExprent(), vars, indent); + splitExprent(dost.getConditionExprent(), vars, indent); + splitExprent(dost.getIncExprent(), vars, indent); + } + else if (dost.getLooptype() == DoStatement.LOOP_WHILE) { + splitExprent(dost.getConditionExprent(), vars, indent); + } + } + + if (stat.getExprents() == null) { + List stats = stat.getStats(); + List> stat_vars = new ArrayList>(stats.size()); + + for (Statement st : stats) { + stat_vars.add(splitVaribles(st, " " + indent)); + } + + for (int x = 0; x < stats.size(); x++) { + switch (stats.get(x).type) { + case Statement.TYPE_DO: { + DoStatement dost = (DoStatement)stats.get(x); + VarVersionPair init = extractVar(dost.getInitExprent()); + if (init != null && (dost.getLooptype() == DoStatement.LOOP_FOR || dost.getLooptype()== DoStatement.LOOP_FOREACH)) { + if (safeToRename(init, stats, stat_vars, x)) { + VarVersionPair newIndex = new VarVersionPair(counters.getCounterAndIncrement(CounterContainer.VAR_COUNTER), 0); + remapVar(init, newIndex, stats.get(x), stat_vars.get(x)); + } + } + break; + } + } + } + + for (Map var : stat_vars) { + for (Entry entry : var.entrySet()) { + if (!vars.containsKey(entry.getKey())) { + vars.put(entry.getKey(), entry.getValue()); + } + } + } + } + else { + for (Exprent e : stat.getExprents()) { + splitExprent(e, vars, indent); + } + } + + if (stat.type == Statement.TYPE_DO) { + DoStatement dost = (DoStatement)stat; + if (dost.getLooptype() == DoStatement.LOOP_DOWHILE) { + splitExprent(dost.getConditionExprent(), vars, indent); + } + } + + //for (Map.Entry entry : vars.entrySet()) { + // System.out.println(indent + " " + (entry.getValue() ? "ass " : "ref ") + entry.getKey()); + //} + + return vars; + } + + private static void splitExprent(Exprent e, Map map, String indent) { + if (e == null) + return; + if (e.type == Exprent.EXPRENT_ASSIGNMENT && ((AssignmentExprent)e).getLeft().type == Exprent.EXPRENT_VAR) { + VarVersionPair var = new VarVersionPair((VarExprent)((AssignmentExprent)e).getLeft()); + if (!map.containsKey(var)) { + map.put(var, true); + } + splitExprent(((AssignmentExprent)e).getRight(), map, indent); + } + else { + for (VarVersionPair var : e.getAllVariables()) { + if (!map.containsKey(var)) { + map.put(var, false); + } + } + } + } + + private static VarVersionPair extractVar(Exprent exp) { + if (exp == null) { + return null; + } + if (exp.type == Exprent.EXPRENT_ASSIGNMENT && ((AssignmentExprent)exp).getLeft().type == Exprent.EXPRENT_VAR) { + return new VarVersionPair((VarExprent)((AssignmentExprent)exp).getLeft()); + } + else if (exp.type == Exprent.EXPRENT_VAR) { + return new VarVersionPair((VarExprent)exp); + } + return null; + } + + private static boolean safeToRename(VarVersionPair var, List stats, List> list, int index) { + for (int x = index + 1; x < list.size(); x++) { + Map map = list.get(x); + if (map.containsKey(var)) { + return map.get(var); + } + } + return false; + } + + private void remapVar(VarVersionPair from, VarVersionPair to, Statement stat, Map stats) { + remapVar(from, to, stat); + varproc.copyVarInfo(from, to); + stats.put(to, true); + stats.remove(from); + } + + private static void remapVar(VarVersionPair from, VarVersionPair to, Statement stat) { + if (stat == null) { + return; + } + if (stat.getExprents() == null) { + for (Object obj : stat.getSequentialObjects()) { + if (obj instanceof Statement) { + remapVar(from, to, (Statement)obj); + } + else if (obj instanceof Exprent) { + remapVar(from, to, (Exprent)obj); + } + } + } + else { + for (Exprent exp : stat.getExprents()) { + remapVar(from, to, exp); + } + } + } + + private static void remapVar(VarVersionPair from, VarVersionPair to, Exprent exprent) { + if (exprent == null) { + return; + } + List lst = exprent.getAllExprents(true); + lst.add(exprent); + + for (Exprent expr : lst) { + if (expr.type == Exprent.EXPRENT_VAR) { + VarExprent var = (VarExprent)expr; + if (var.getIndex() == from.var && var.getVersion() == from.version) { + var.setIndex(to.var); + var.setVersion(to.version); + } + } + } + } + + private void setupLVTs(Statement stat) { + if (stat == null || varproc.getLVT() != null) { + return; + } + + Map vars = varproc.getLVT().getVars(stat); + if (stat.getExprents() == null) { + for (Object obj : stat.getSequentialObjects()) { + if (obj instanceof Statement) { + setupLVTs((Statement)obj); + } + else if (obj instanceof Exprent) { + setupLVTs((Exprent)obj, vars); + } + } + } + else { + for (Exprent exp : stat.getExprents()) { + setupLVTs(exp, vars); + } + } + } + + private void setupLVTs(Exprent exprent, Map lvts) { + if (exprent == null) { + return; + } + List lst = exprent.getAllExprents(true); + lst.add(exprent); + + for (Exprent expr : lst) { + if (expr.type == Exprent.EXPRENT_VAR) { + VarExprent var = (VarExprent)expr; + int index = varproc.getRemapped(var.getIndex()); + LVTVariable lvt = lvts.get(index); + if (lvt != null) { + var.setLVT(lvt); + } + } + } + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java index 0a619d0..a8ad019 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java @@ -160,8 +160,8 @@ public LocalVariableTable getLVT() { return this.lvt; } - public LVTVariable findLVT(int index, List instructionOffsets) { - return this.lvt == null ? null : lvt.find(index, instructionOffsets); + public LVTVariable findLVT(int index, Statement stat) { + return this.lvt == null ? null : lvt.find(index, stat); } public int getRemapped(int index) { @@ -169,4 +169,11 @@ public int getRemapped(int index) { if (res == null) return index; return res.var; } + + public void copyVarInfo(VarVersionPair from, VarVersionPair to) { + setVarName(to, getVarName(from)); + setVarFinal(to, getVarFinal(from)); + setVarType(to, getVarType(from)); + varVersions.getMapOriginalVarIndices().put(to.var, varVersions.getMapOriginalVarIndices().get(from.var)); + } } From 9f908e30826e71247955a0c38f1154d8065073ec Mon Sep 17 00:00:00 2001 From: cpw Date: Thu, 27 Aug 2015 15:39:39 -0400 Subject: [PATCH 16/73] Lex's tests --- .../decompiler/DecompilerTestFixture.java | 3 +- .../jetbrains/java/decompiler/LVTTest.java | 7 ++ .../decompiler/SingleClassesTestBase.java | 2 +- .../classes/pkg/TestLexManosLVT$Bob.class | Bin 0 -> 1472 bytes testData/classes/pkg/TestLexManosLVT.class | Bin 0 -> 1967 bytes testData/results/TestLexManosLVT.dec | 64 ++++++++++++++++++ testData/src/pkg/TestLexManosLVT.java | 64 ++++++++++++++++++ 7 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 testData/classes/pkg/TestLexManosLVT$Bob.class create mode 100644 testData/classes/pkg/TestLexManosLVT.class create mode 100644 testData/results/TestLexManosLVT.dec create mode 100644 testData/src/pkg/TestLexManosLVT.java diff --git a/test/org/jetbrains/java/decompiler/DecompilerTestFixture.java b/test/org/jetbrains/java/decompiler/DecompilerTestFixture.java index d0d869c..f2c344a 100644 --- a/test/org/jetbrains/java/decompiler/DecompilerTestFixture.java +++ b/test/org/jetbrains/java/decompiler/DecompilerTestFixture.java @@ -31,6 +31,7 @@ public class DecompilerTestFixture { private File tempDir; private File targetDir; private ConsoleDecompiler decompiler; + public boolean cleanup = true; public void setUp() throws IOException { setUp(Collections.emptyMap()); @@ -62,7 +63,7 @@ public void setUp(final Map options) throws IOException { } public void tearDown() { - if (tempDir != null) { + if (tempDir != null && cleanup) { delete(tempDir); } } diff --git a/test/org/jetbrains/java/decompiler/LVTTest.java b/test/org/jetbrains/java/decompiler/LVTTest.java index 983368c..f4d869c 100644 --- a/test/org/jetbrains/java/decompiler/LVTTest.java +++ b/test/org/jetbrains/java/decompiler/LVTTest.java @@ -18,6 +18,7 @@ import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.junit.Test; +import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -29,6 +30,12 @@ protected Map getDecompilerOptions() { }}; } + @Override + public void setUp() throws IOException { + super.setUp(); + fixture.cleanup = false; + } + @Test public void testMatchLM() { doTest("pkg/TestLexManosLVT"); } @Test public void testMatch1() { doTest("pkg/TestLVT"); } @Test public void testMatch2() { doTest("pkg/TestLVTScoping"); } } diff --git a/test/org/jetbrains/java/decompiler/SingleClassesTestBase.java b/test/org/jetbrains/java/decompiler/SingleClassesTestBase.java index 165f695..064ca5c 100644 --- a/test/org/jetbrains/java/decompiler/SingleClassesTestBase.java +++ b/test/org/jetbrains/java/decompiler/SingleClassesTestBase.java @@ -32,7 +32,7 @@ import static org.junit.Assert.assertTrue; public abstract class SingleClassesTestBase { - private DecompilerTestFixture fixture; + protected DecompilerTestFixture fixture; @Before public void setUp() throws IOException { diff --git a/testData/classes/pkg/TestLexManosLVT$Bob.class b/testData/classes/pkg/TestLexManosLVT$Bob.class new file mode 100644 index 0000000000000000000000000000000000000000..38c9bd23ae2a768cab887a845c780bfd31e4c560 GIT binary patch literal 1472 zcmaJ>-%k@+6#h=9?R44^urgpNtL`o;En>B5b=$&%$YzNJOp1-oK26KWj+PnHnW{XR z@aCib1H4G~O)*)CW*Z;&Y5xxY4(qvBkglPoncjQO{mys4^JC_}Pw(0QvY1y9APjFj zT+6Ijo?o(_ESiq%l`1P^H{F_wAffMp`N+&POlK`K@3htlf!`MhiBgZ40s~6RgxLJj z-NmuWZ0YWNnK1Ilckq#7yCVONyT9!u`9Z&UErr{LMs5ng+ z=(=&!^R0CqKjJ5r>$ZG?@kNmBX6~>WpEX(LdLe=_&SchGy&uInjH@`$0wvG?fjUx1 z6Vwe6+i;lfhy+Dd`b)NBEw$EbR&&LyH7r8BY8gZ_)P#acK+&rD25r;Ui@i@p3h|3#0r7 z!+S_nm)cWGaR>J%liAzC&a3TQSjmNUP)MjoD4P#TZ>*h*DmiTj$wbW1viV3l5qyc* zE`Esr{5Q^SX=G?dbi4C$D?~=f2yb`(-6BekZg>8AE%Lww|EF0oa21n0BvZJKGWW~I zFL05^W8Oby-!ndYj%i$A-b4Fj}Uzjts(T5E0BDlnU3u=c+f*?|H8R#I6 zh{~2Uzd8-*2hM}_AYw4k;hG*1qaCs$Aif)4%FVYZvOQ{gV)QMtBEx{pFw6kpid3)5 zy~>tQuZb_ket3?1D!Mzgi{a{RCEczTMROLF7lOVA;ll$xJIp00!JLjJK^zXMj z0QxYm!X?nOy1W!ioB2Z0+!!~qxqNaetwIq9+%@hQv5b*jicQYnH5Up3$2wEfiS+2$ z?ey?S@^U9FJ72Qw9LdcKxMmXq-a#vC6@~=dk?53wGMZa7HMmiuLxoSEK51pmiS?Cv z(@q=n8B?G!nOiV2Q-*EHb6Kbq?pS$&U~(Tn*YagmC zI3k#pU5ZDiR2;6@uNSOLY}mGq&7_qtXlO@`oQ~?~#4!P%RWNO%kh29^BGD3-W0EM# z<25*esEP==aU81ps-p`h>3P=NU~J75ggqH^>`&?FL5zOiG4d0RDid;3gFvqi5Bi7@ ziOwecID>u_ZwR#R!?et+juXLQ}Ld_fftW(B9~fUxO2raGmDpPTNaI$%B|ZA<`qjmp5~Wc zVvn>G&=c9LX^&=%d|uw|W{S{3|J-Z=BA`hpxpPy(N6FdGQ;e(N>gjp{?JI6v&^dcc zLez4vm-lt3=gE&i#n|;ZwFx3l`<`i6yJiNIogw%6E!2dRaL?d;|-@Zfp%oB7ZPj2JL#1^zu-{82wIG1jLPm0gW@>^wjKzpVQC?RELz_-({ zy8FFbs15mo-kyHXeXj_5gX)9gpH-iO<U#ye$|bn|o>R~Y^!{EoLU z#@#Z~lqE>V8Yb{I^%Q#J!&T~ei0wuagQ)MrNH&A4c_LoE|+!XGy@HaOU$xf9;5fQk`5YW^*SKUAIe4{4d zliiQk%aZ@lc><8BPS5-R< x1 = new ArrayList(); + for(Object y : x1) { + ; + } + for(Object y : x1) { + int[] x2 = new int[10]; + for(int y2 : x2) { + ; + + } + for(int y2 : x2) { + System.out.println("asdf"); + + } + System.out.println("asdf"); + + } + switch(Bob.HI) { + case HI: System.out.println("HI"); + break; + case LO: + System.out.println("LO"); + } + + if(TestLexManosLVT.Bob.HI == TestLexManosLVT.Bob.HI) { + String a = "a"; + } else { + String b = "b"; } + String a2; + if(TestLexManosLVT.Bob.HI == TestLexManosLVT.Bob.HI) { + a2 = "a"; + } else { a2 = "b"; + } + if (TestLexManosLVT.Bob.HI == TestLexManosLVT.Bob.HI) { + a2 = "a"; + } + System.out.println(a2); } private static enum Bob { HI, LO; + static { + for(TestLexManosLVT.Bob b : values()) { + for(TestLexManosLVT.Bob c : values()) { + for(TestLexManosLVT.Bob d : values()) { + if(b == c) { + System.out.println("Asdf"); + } + } + } + } + + } + } +} diff --git a/testData/src/pkg/TestLexManosLVT.java b/testData/src/pkg/TestLexManosLVT.java new file mode 100644 index 0000000..e04de1e --- /dev/null +++ b/testData/src/pkg/TestLexManosLVT.java @@ -0,0 +1,64 @@ +package pkg; + +import java.util.ArrayList; + +public class TestLexManosLVT { + public static void main() { + int[] x = new int[5]; + for (int y : x) + ; + for (int y : x) { + System.out.println("asdf"); + } + ArrayList x1 = new ArrayList(); + for (Object y : x1) + ; + for (Object y : x1) { + int[] x2 = new int[10]; + for (int y2 : x2) + ; + for (int y2 : x2) { + System.out.println("asdf"); + } + System.out.println("asdf"); + } + switch (Bob.HI) { + case HI: + System.out.println("HI"); + break; + case LO: + System.out.println("LO"); + break; + } + if (Bob.HI == Bob.HI) { + String a = "a"; + } else { + String b = "b"; + } + String a2; + if (Bob.HI == Bob.HI) { + a2 = "a"; + } else { + a2 = "b"; + } + if (Bob.HI == Bob.HI) { + a2 = "a"; + } + System.out.println(a2); + + } + + private static enum Bob { + HI, LO; + static { + for (Bob b : Bob.values()) { + for (Bob c : values()) { + for (Bob d : values()) { + if (b == c) + System.out.println("Asdf"); + } + } + } + }; + } +} From f023a4410692d9e353b0ddd61d3072df16fdd18a Mon Sep 17 00:00:00 2001 From: cpw Date: Thu, 27 Aug 2015 15:41:47 -0400 Subject: [PATCH 17/73] Fix inverted null test --- .../decompiler/modules/decompiler/vars/VarDefinitionHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java index 1e1a4a7..d24813f 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java @@ -556,7 +556,7 @@ private static void remapVar(VarVersionPair from, VarVersionPair to, Exprent exp } private void setupLVTs(Statement stat) { - if (stat == null || varproc.getLVT() != null) { + if (stat == null || varproc.getLVT() == null) { return; } From de3a686365bc75c89bf8a1cf47d8c9cb9266b7df Mon Sep 17 00:00:00 2001 From: cpw Date: Fri, 11 Sep 2015 21:57:09 -0400 Subject: [PATCH 18/73] More tweaks. Closer! --- .../java/decompiler/code/cfg/BasicBlock.java | 15 +++++ .../main/rels/MethodProcessorRunnable.java | 2 +- .../modules/decompiler/PPandMMHelper.java | 65 ++++++++++++++++++- .../modules/decompiler/stats/Statement.java | 10 ++- .../modules/decompiler/vars/LVTVariable.java | 22 +++---- .../decompiler/vars/LocalVariableTable.java | 61 ++++++++--------- .../decompiler/vars/VarDefinitionHelper.java | 60 ++++++++++++----- .../decompiler/vars/VarVersionsProcessor.java | 4 +- testData/results/TestLVT.dec | 6 +- testData/results/TestLVTScoping.dec | 6 +- testData/results/TestLexManosLVT.dec | 14 ++-- 11 files changed, 187 insertions(+), 78 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/code/cfg/BasicBlock.java b/src/org/jetbrains/java/decompiler/code/cfg/BasicBlock.java index 2e29529..a36047b 100644 --- a/src/org/jetbrains/java/decompiler/code/cfg/BasicBlock.java +++ b/src/org/jetbrains/java/decompiler/code/cfg/BasicBlock.java @@ -262,4 +262,19 @@ public List getPredExceptions() { public void setPredExceptions(List predExceptions) { this.predExceptions = predExceptions; } + + public int getStartInstruction() { + if (seq.isEmpty()) { + return 0; + } + return instrOldOffsets.get(0); + } + + public int getEndInstruction() { + if (seq.isEmpty()) { + return 0; + } + int end = seq.getLastInstr().length(); + return end + instrOldOffsets.get(size() -1); + } } diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java index 54a77e1..5844b93 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java @@ -153,7 +153,7 @@ public static RootStatement codeToJava(StructMethod mt, VarProcessor varProc) th varProc.setVarVersions(root); - if (!new PPandMMHelper().findPPandMM(root)) { + if (!new PPandMMHelper(varProc).findPPandMM(root)) { break; } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java index 4eb366b..d93bad6 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java @@ -19,19 +19,31 @@ import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph; import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode; import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper; import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Map; public class PPandMMHelper { private boolean exprentReplaced; + private VarProcessor varProc; + private Map remaps = new HashMap(); + + public PPandMMHelper(VarProcessor varProc) { + this.varProc = varProc; + } public boolean findPPandMM(RootStatement root) { @@ -59,6 +71,8 @@ public boolean findPPandMM(RootStatement root) { stack.addAll(node.succs); } + updateVersions(dgraph); + return res; } @@ -134,13 +148,16 @@ private Exprent processExprentRecursive(Exprent exprent) { Exprent left = as.getLeft(); VarType condtype = econd.getExprType(); - if (left.equals(econd) && (midlayer == null || midlayer.equals(condtype))) { + if (exprsEqual(left, econd) && (midlayer == null || midlayer.equals(condtype))) { FunctionExprent ret = new FunctionExprent( func.getFuncType() == FunctionExprent.FUNCTION_ADD ? FunctionExprent.FUNCTION_PPI : FunctionExprent.FUNCTION_MMI, econd, func.bytecode); ret.setImplicitType(condtype); exprentReplaced = true; + if (!left.equals(econd)) { + remaps.put(new VarVersionPair((VarExprent)left), new VarVersionPair((VarExprent)econd)); + } return ret; } } @@ -150,4 +167,48 @@ private Exprent processExprentRecursive(Exprent exprent) { return null; } -} + + private boolean exprsEqual(Exprent e1, Exprent e2) { + if (e1 == e2) return true; + if (e1 == null || e2 == null) return false; + if (e1.type == VarExprent.EXPRENT_VAR) { + return varsEqual(e1, e2); + } + return e1.equals(e2); + } + + private boolean varsEqual(Exprent e1, Exprent e2) { + if (!(e1 instanceof VarExprent)) return false; + if (!(e2 instanceof VarExprent)) return false; + + VarExprent v1 = (VarExprent)e1; + VarExprent v2 = (VarExprent)e2; + return varProc.getRemapped(v1.getIndex()) == varProc.getRemapped(v2.getIndex()) + && InterpreterUtil.equalObjects(v1.getVarType(), v2.getVarType()); + } + + + private void updateVersions(DirectGraph graph) { + if (remaps.isEmpty()) return; + graph.iterateExprents(new DirectGraph.ExprentIterator() { + @Override + public int processExprent(Exprent exprent) { + List lst = exprent.getAllExprents(true); + lst.add(exprent); + + for (Exprent expr : lst) { + if (expr.type == Exprent.EXPRENT_VAR) { + VarExprent var = (VarExprent)expr; + VarVersionPair nvar = remaps.get(new VarVersionPair(var)); + if (nvar != null) { + var.setIndex(nvar.var); + var.setVersion(nvar.version); + } + } + } + + return 0; + } + }); + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java index ced701f..5a44fcf 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java @@ -862,9 +862,9 @@ public void setCopied(boolean copied) { // helper methods public String toString() { - return id.toString(); + return String.format("{%d}:%d", type, id); } - + // ***************************************************************************** // IMatchable implementation // ***************************************************************************** @@ -937,5 +937,9 @@ public boolean match(MatchNode matchNode, MatchEngine engine) { return true; } - + +public SequenceStatement getParentSequenceStat() { + return (SequenceStatement) (getParent()!=null && getParent().type == TYPE_SEQUENCE ? getParent() : null); +} + } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LVTVariable.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LVTVariable.java index dc7783c..bc7ba23 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LVTVariable.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LVTVariable.java @@ -42,34 +42,34 @@ void merge(LVTVariable other) { public boolean equals(Object obj) { if (!(obj instanceof LVTVariable)) return false; - return ((LVTVariable) obj).index == index && ((LVTVariable) obj).start == start; + return ((LVTVariable) obj).index == index && ((LVTVariable) obj).end == end; } @Override public int hashCode() { - return index * 31 + start; + return index * 31 + end; } - public void addTo(Map> startpoints) { - Set starts = startpoints.get(this.start); - if (starts == null) { - starts = new HashSet(); - startpoints.put(this.start, starts); + public void addTo(Map> endpoints) { + Set ends = endpoints.get(this.end); + if (ends == null) { + ends = new HashSet(); + endpoints.put(this.end, ends); } - starts.add(this); + ends.add(this); } @Override public int compareTo(LVTVariable o) { - if (o.start > start) return -1; - if (o.start < start) return 1; + if (o.end > end) return -1; + if (o.end < end) return 1; if (o.index > index) return -1; if (o.index < index) return 1; return 0; } @Override public String toString() { - return "\'("+index+","+start+")"+desc+(sig!=null ? "<"+sig+"> ":" ")+name+"\'"; + return "\'("+index+","+end+")"+desc+(sig!=null ? "<"+sig+"> ":" ")+name+"\'"; } public String getDesc() { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java index cd56bac..f7e1858 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java @@ -1,29 +1,37 @@ package org.jetbrains.java.decompiler.modules.decompiler.vars; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.BitSet; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.Stack; +import org.jetbrains.java.decompiler.code.Instruction; +import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.main.rels.MethodProcessorRunnable; +import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; +import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; public class LocalVariableTable { - private Map> startpoints; + private Map> endpoints; private ArrayList allLVT; private Map> mapLVT; public LocalVariableTable(int len) { - startpoints = new HashMap>(len); + endpoints = new HashMap>(len); allLVT = new ArrayList(len); } public void addVariable(LVTVariable v) { allLVT.add(v); - v.addTo(startpoints); + v.addTo(endpoints); } public void mergeLVTs(LocalVariableTable otherLVT) { @@ -88,39 +96,32 @@ public List getCandidates(int index) { return getMapVarNames().get(index); } - public List getVars(int index, int start, int end) { - if (!getMapVarNames().containsKey(index)) { - return null; - } - - List ret = new ArrayList(); - for (LVTVariable lvt : getMapVarNames().get(index)) { - if (lvt.start >= start && lvt.end <= end) { - ret.add(lvt); + public Map getVars(SequenceStatement sequenceStatement) { + if (sequenceStatement == null) return new HashMap(); + int start = Integer.MAX_VALUE; + int end = Integer.MIN_VALUE; + for (Statement st : sequenceStatement.getStats()) { + start = Math.min(start, st.getBasichead().getBlock().getStartInstruction()); + end = Math.max(end, st.getBasichead().getBlock().getEndInstruction()); } - } - - return ret; - } - - public Map getVars(Statement stat) { - BitSet values = new BitSet(); - MethodProcessorRunnable.getOffset(stat, values); - int start = values.nextSetBit(0); - int end = values.length()-1; //System.out.println(indent + stat.getClass().getSimpleName() + " (" + start +", " + end + ")"); Map ret = new HashMap(); - for (Entry> entry : getMapVarNames().entrySet()) { - for (LVTVariable lvt : entry.getValue()) { - if (lvt.start >= start && lvt.end <= end) { - if (ret.containsKey(entry.getKey())) { - System.out.println("DUPLICATE INDEX WHAT THE FUCK: " + entry.getKey()); - } - ret.put(entry.getKey(), lvt); + if (endpoints.containsKey(end)) { + for (LVTVariable lvt : endpoints.get(end)) { + ret.put(lvt.index,lvt); } - } } +// for (Entry> entry : endpoints.get(end)) { +// for (LVTVariable lvt : entry.getValue()) { +// if (lvt.start >= start && lvt.end <= end) { +// if (ret.containsKey(entry.getKey())) { +// System.out.println("DUPLICATE INDEX WHAT THE FUCK: " + entry.getKey()); +// } +// ret.put(entry.getKey(), lvt); +// } +// } +// } return ret; } } \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java index d24813f..d68ee36 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java @@ -16,6 +16,7 @@ package org.jetbrains.java.decompiler.modules.decompiler.vars; import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector; @@ -23,9 +24,13 @@ import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper; +import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructMethod; @@ -47,12 +52,13 @@ public class VarDefinitionHelper { private final CounterContainer counters = DecompilerContext.getCounterContainer(); + private final Map> scopedVVPs; public VarDefinitionHelper(Statement root, StructMethod mt, VarProcessor varproc) { mapVarDefStatements = new HashMap(); mapStatementVars = new HashMap>(); implDefVars = new HashSet(); - + scopedVVPs = new HashMap>(); this.varproc = varproc; VarNamesCollector vc = DecompilerContext.getVarNamesCollector(); @@ -70,10 +76,21 @@ public VarDefinitionHelper(Statement root, StructMethod mt, VarProcessor varproc // method parameters are implicitly defined int varindex = 0; + Statement seq = root.getFirst(); + SequenceStatement seqStat = null; + if (seq.type == Statement.TYPE_SEQUENCE) { + seqStat = (SequenceStatement) seq; + } for (int i = 0; i < paramcount; i++) { implDefVars.add(varindex); - varproc.setVarName(new VarVersionPair(varindex, 0), vc.getFreeName(varindex)); - + VarVersionPair vvp = new VarVersionPair(varindex, 0); + varproc.setVarName(vvp, vc.getFreeName(varindex)); + if (seqStat != null) { + if (scopedVVPs.get(seqStat) == null) { + scopedVVPs.put(seqStat,new ArrayList()); + } + scopedVVPs.get(seqStat).add(vvp); + } if (thisvar) { if (i == 0) { varindex++; @@ -115,7 +132,8 @@ else if (st.type == Statement.TYPE_TRYCATCH) { if (lstVars != null) { for (VarExprent var : lstVars) { implDefVars.add(var.getIndex()); - varproc.setVarName(new VarVersionPair(var), vc.getFreeName(var.getIndex())); + VarVersionPair pair = new VarVersionPair(var); + varproc.setVarName(pair, vc.getFreeName(var.getIndex())); var.setDefinition(true); } } @@ -127,12 +145,18 @@ else if (st.type == Statement.TYPE_TRYCATCH) { } public void setVarDefinitions() { + FlattenStatementsHelper flattenHelper = new FlattenStatementsHelper(); + DirectGraph graph = flattenHelper.buildDirectGraph(root); VarNamesCollector vc = DecompilerContext.getVarNamesCollector(); for (Entry en : mapVarDefStatements.entrySet()) { Statement stat = en.getValue(); Integer index = en.getKey(); + int newindex = varproc.getRemapped(index); + if (index.intValue() != newindex) { + // remerge + } setupLVTs(stat); @@ -215,17 +239,9 @@ else if (first.getExprents() == null) { var.setDefinition(true); if (varproc.getLVT() != null) { - BitSet values = new BitSet(); - MethodProcessorRunnable.getOffset(stat, values); - int start = values.nextSetBit(0); - int end = values.length()-1; - List vars = varproc.getLVT().getVars(index, start, end); - if (vars != null) { - if (vars.size() == 1) { - var.setLVT(vars.get(0)); - } - // ToDo: If this is >1 then we need to decrease the scope of these variables. - //if this is = 0 and we have lvts for this... then we need to expand the scope... + Map vars = varproc.getLVT().getVars(stat.getParentSequenceStat()); + if (vars.containsKey(var.getIndex())) { + var.setLVT(vars.get(var.getIndex())); } } @@ -393,12 +409,17 @@ private static boolean setDefinition(Exprent expr, Integer index) { private Map splitVaribles(Statement stat, String indent) { Map vars = new HashMap(); + SequenceStatement seqStat = stat.getParentSequenceStat(); //BitSet values = new BitSet(); //MethodProcessorRunnable.getOffset(stat, values); //int start = values.nextSetBit(0); //int end = values.length()-1; //System.out.println(indent + stat.getClass().getSimpleName() + " (" + start +", " + end + ")"); - + if (seqStat != null) { + if (!scopedVVPs.containsKey(stat.getBasichead())) { + scopedVVPs.put(seqStat, new ArrayList()); + } + } if (stat.type == Statement.TYPE_DO) { DoStatement dost = (DoStatement)stat; if (dost.getLooptype() == DoStatement.LOOP_FOREACH) { @@ -443,6 +464,9 @@ else if (dost.getLooptype() == DoStatement.LOOP_WHILE) { for (Entry entry : var.entrySet()) { if (!vars.containsKey(entry.getKey())) { vars.put(entry.getKey(), entry.getValue()); + if (seqStat != null) { + scopedVVPs.get(seqStat).add(entry.getKey()); + } } } } @@ -560,7 +584,9 @@ private void setupLVTs(Statement stat) { return; } - Map vars = varproc.getLVT().getVars(stat); + Map vars = varproc.getLVT().getVars(stat.getParentSequenceStat()); + List vvps = scopedVVPs.get(stat.getBasichead()); + if (stat.getExprents() == null) { for (Object obj : stat.getSequentialObjects()) { if (obj instanceof Statement) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java index cb37d4d..d56c778 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java @@ -53,7 +53,7 @@ public void setVarVersions(RootStatement root) { typeProcessor = new VarTypeProcessor(); typeProcessor.calculateVarTypes(root, graph); - simpleMerge(typeProcessor, graph, mt); +// simpleMerge(typeProcessor, graph, mt); // FIXME: advanced merging @@ -103,7 +103,7 @@ private static void mergePhiVersions(SSAConstructorSparseEx ssa, DirectGraph gra updateVersions(graph, phiVersions); } - private static void updateVersions(DirectGraph graph, final Map versions) { + public static void updateVersions(DirectGraph graph, final Map versions) { graph.iterateExprents(new DirectGraph.ExprentIterator() { @Override public int processExprent(Exprent exprent) { diff --git a/testData/results/TestLVT.dec b/testData/results/TestLVT.dec index d161f07..aed09b4 100644 --- a/testData/results/TestLVT.dec +++ b/testData/results/TestLVT.dec @@ -14,9 +14,9 @@ public class TestLVT { String scope2a = "scope2a"; ArrayList noise = new ArrayList(); String spam = scope1 + scope2 + scope2a + i + noise; - System.out.println(spam); - } - for (long i = 0L; i < 10L; ++i) { + System.out.println(spam); } + + for(long i = 0L; i < 10L; ++i) { String scope2 = "scope2+1"; String scope2a = "scope2+1a"; HashMap noise = new HashMap(); diff --git a/testData/results/TestLVTScoping.dec b/testData/results/TestLVTScoping.dec index 7bbf624..2949d31 100644 --- a/testData/results/TestLVTScoping.dec +++ b/testData/results/TestLVTScoping.dec @@ -12,11 +12,11 @@ public class TestLVTScoping { System.out.println(a); } - public static void method2() { String a; + public static void method2() { if(1 == Integer.valueOf(1).intValue()) { - a = "YAY"; + String a = "YAY"; } else { - a = "NAY"; + String a = "NAY"; System.out.println(a); } } diff --git a/testData/results/TestLexManosLVT.dec b/testData/results/TestLexManosLVT.dec index 9ef5e62..285cb14 100644 --- a/testData/results/TestLexManosLVT.dec +++ b/testData/results/TestLexManosLVT.dec @@ -29,26 +29,28 @@ public class TestLexManosLVT { } switch(Bob.HI) { - case HI: System.out.println("HI"); + case HI: + System.out.println("HI"); break; case LO: - System.out.println("LO"); - } + System.out.println("LO"); } if(TestLexManosLVT.Bob.HI == TestLexManosLVT.Bob.HI) { String a = "a"; } else { - String b = "b"; } + String b = "b"; + } String a2; if(TestLexManosLVT.Bob.HI == TestLexManosLVT.Bob.HI) { a2 = "a"; } else { a2 = "b"; } if (TestLexManosLVT.Bob.HI == TestLexManosLVT.Bob.HI) { - a2 = "a"; - } + a2 = "a"; } System.out.println(a2); } private static enum Bob { HI, LO; static { + + for(TestLexManosLVT.Bob b : values()) { for(TestLexManosLVT.Bob c : values()) { for(TestLexManosLVT.Bob d : values()) { From d828cac2aee189062bac3731431f33e2a6ba30ca Mon Sep 17 00:00:00 2001 From: cpw Date: Fri, 11 Sep 2015 23:42:09 -0400 Subject: [PATCH 19/73] More tweaks, it passes the TestLVT tests --- .../modules/decompiler/stats/Statement.java | 20 ++++++++------ .../decompiler/vars/VarDefinitionHelper.java | 27 ++++++++++++------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java index 5a44fcf..c126050 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java @@ -868,11 +868,11 @@ public String toString() { // ***************************************************************************** // IMatchable implementation // ***************************************************************************** - + public IMatchable findObject(MatchNode matchNode, int index) { - + int node_type = matchNode.getType(); - + if (node_type == MatchNode.MATCHNODE_STATEMENT && !this.stats.isEmpty()) { String position = (String)matchNode.getRuleValue(MatchProperties.STATEMENT_POSITION); if(position != null) { @@ -897,11 +897,11 @@ public IMatchable findObject(MatchNode matchNode, int index) { } public boolean match(MatchNode matchNode, MatchEngine engine) { - + if(matchNode.getType() != MatchNode.MATCHNODE_STATEMENT) { return false; } - + for(Entry rule : matchNode.getRules().entrySet()) { switch(rule.getKey()) { case STATEMENT_TYPE: @@ -932,14 +932,18 @@ public boolean match(MatchNode matchNode, MatchEngine engine) { } break; } - + } - + return true; } + private SequenceStatement parentSeqStat; public SequenceStatement getParentSequenceStat() { - return (SequenceStatement) (getParent()!=null && getParent().type == TYPE_SEQUENCE ? getParent() : null); + if (parentSeqStat == null) { + parentSeqStat = (getParent()!=null && getParent().type == TYPE_SEQUENCE) ? (SequenceStatement)getParent() : new SequenceStatement(Arrays.asList(this)); + } + return parentSeqStat; } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java index d68ee36..db41fb8 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java @@ -30,6 +30,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; +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.StructClass; @@ -53,6 +54,8 @@ public class VarDefinitionHelper { private final CounterContainer counters = DecompilerContext.getCounterContainer(); private final Map> scopedVVPs; + +private RootStatement rootStmt; public VarDefinitionHelper(Statement root, StructMethod mt, VarProcessor varproc) { mapVarDefStatements = new HashMap(); @@ -60,6 +63,7 @@ public VarDefinitionHelper(Statement root, StructMethod mt, VarProcessor varproc implDefVars = new HashSet(); scopedVVPs = new HashMap>(); this.varproc = varproc; + this.rootStmt = (RootStatement) root; VarNamesCollector vc = DecompilerContext.getVarNamesCollector(); @@ -145,19 +149,17 @@ else if (st.type == Statement.TYPE_TRYCATCH) { } public void setVarDefinitions() { - FlattenStatementsHelper flattenHelper = new FlattenStatementsHelper(); - DirectGraph graph = flattenHelper.buildDirectGraph(root); - VarNamesCollector vc = DecompilerContext.getVarNamesCollector(); + Map> trackingMap = new HashMap>(); for (Entry en : mapVarDefStatements.entrySet()) { Statement stat = en.getValue(); + if (!trackingMap.containsKey(stat.getParentSequenceStat())) { + trackingMap.put(stat.getParentSequenceStat(), new HashMap()); + } + Map scopedMap = trackingMap.get(stat.getParentSequenceStat()); Integer index = en.getKey(); int newindex = varproc.getRemapped(index); - if (index.intValue() != newindex) { - // remerge - } - setupLVTs(stat); if (implDefVars.contains(index)) { @@ -172,7 +174,7 @@ public void setVarDefinitions() { DoStatement dstat = (DoStatement)stat; if (dstat.getLooptype() == DoStatement.LOOP_FOR) { - if (dstat.getInitExprent() != null && setDefinition(dstat.getInitExprent(), index)) { + if (dstat.getInitExprent() != null && setDefinition(dstat.getInitExprent(), index, scopedMap)) { continue; } else { @@ -215,7 +217,11 @@ else if (first.getExprents() == null) { int addindex = 0; for (Exprent expr : lst) { - if (setDefinition(expr, index)) { + if (scopedMap.containsKey(newindex)) { + defset = true; + break; + } + if (setDefinition(expr, index, scopedMap)) { defset = true; break; } @@ -386,13 +392,14 @@ private static List getAllVars(List lst) { return res; } - private static boolean setDefinition(Exprent expr, Integer index) { + private static boolean setDefinition(Exprent expr, Integer index, Map stats) { if (expr.type == Exprent.EXPRENT_ASSIGNMENT) { Exprent left = ((AssignmentExprent)expr).getLeft(); if (left.type == Exprent.EXPRENT_VAR) { VarExprent var = (VarExprent)left; if (var.getIndex() == index.intValue()) { var.setDefinition(true); + stats.put(index, var); return true; } } From 9ac4c6fcc3a1c13833ad17c41d6c6218ec9059bb Mon Sep 17 00:00:00 2001 From: cpw Date: Sat, 12 Sep 2015 00:00:42 -0400 Subject: [PATCH 20/73] Another fix from lex, merges RHS and LHS of exprents --- .../decompiler/vars/VarDefinitionHelper.java | 241 +++++------------- 1 file changed, 61 insertions(+), 180 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java index db41fb8..5a276cd 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java @@ -16,21 +16,14 @@ package org.jetbrains.java.decompiler.modules.decompiler.vars; import org.jetbrains.java.decompiler.code.CodeConstants; -import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.main.DecompilerContext; -import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector; -import org.jetbrains.java.decompiler.main.rels.MethodProcessorRunnable; import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; -import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph; -import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper; -import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; -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.StructClass; @@ -39,6 +32,7 @@ import java.util.*; import java.util.Map.Entry; +import java.util.AbstractMap.SimpleImmutableEntry; public class VarDefinitionHelper { @@ -51,19 +45,12 @@ public class VarDefinitionHelper { private final VarProcessor varproc; - private final CounterContainer counters = DecompilerContext.getCounterContainer(); - - private final Map> scopedVVPs; - -private RootStatement rootStmt; public VarDefinitionHelper(Statement root, StructMethod mt, VarProcessor varproc) { mapVarDefStatements = new HashMap(); mapStatementVars = new HashMap>(); implDefVars = new HashSet(); - scopedVVPs = new HashMap>(); this.varproc = varproc; - this.rootStmt = (RootStatement) root; VarNamesCollector vc = DecompilerContext.getVarNamesCollector(); @@ -80,21 +67,10 @@ public VarDefinitionHelper(Statement root, StructMethod mt, VarProcessor varproc // method parameters are implicitly defined int varindex = 0; - Statement seq = root.getFirst(); - SequenceStatement seqStat = null; - if (seq.type == Statement.TYPE_SEQUENCE) { - seqStat = (SequenceStatement) seq; - } for (int i = 0; i < paramcount; i++) { implDefVars.add(varindex); VarVersionPair vvp = new VarVersionPair(varindex, 0); varproc.setVarName(vvp, vc.getFreeName(varindex)); - if (seqStat != null) { - if (scopedVVPs.get(seqStat) == null) { - scopedVVPs.put(seqStat,new ArrayList()); - } - scopedVVPs.get(seqStat).add(vvp); - } if (thisvar) { if (i == 0) { varindex++; @@ -116,7 +92,7 @@ public VarDefinitionHelper(Statement root, StructMethod mt, VarProcessor varproc vc.addName("this"); } - splitVaribles(root, ""); + mergeVars(root); // catch variables are implicitly defined LinkedList stack = new LinkedList(); @@ -407,211 +383,117 @@ private static boolean setDefinition(Exprent expr, Integer index, Map splitVaribles(Statement stat, String indent) { - Map vars = new HashMap(); - - SequenceStatement seqStat = stat.getParentSequenceStat(); - //BitSet values = new BitSet(); - //MethodProcessorRunnable.getOffset(stat, values); - //int start = values.nextSetBit(0); - //int end = values.length()-1; - //System.out.println(indent + stat.getClass().getSimpleName() + " (" + start +", " + end + ")"); - if (seqStat != null) { - if (!scopedVVPs.containsKey(stat.getBasichead())) { - scopedVVPs.put(seqStat, new ArrayList()); - } - } - if (stat.type == Statement.TYPE_DO) { - DoStatement dost = (DoStatement)stat; - if (dost.getLooptype() == DoStatement.LOOP_FOREACH) { - vars.put(new VarVersionPair((VarExprent)dost.getInitExprent()), true); - splitExprent(dost.getIncExprent(), vars, indent); - } - else if (dost.getLooptype() == DoStatement.LOOP_FOR) { - splitExprent(dost.getInitExprent(), vars, indent); - splitExprent(dost.getConditionExprent(), vars, indent); - splitExprent(dost.getIncExprent(), vars, indent); - } - else if (dost.getLooptype() == DoStatement.LOOP_WHILE) { - splitExprent(dost.getConditionExprent(), vars, indent); - } + private void setupLVTs(Statement stat) { + if (stat == null || varproc.getLVT() == null) { + return; } - if (stat.getExprents() == null) { - List stats = stat.getStats(); - List> stat_vars = new ArrayList>(stats.size()); - - for (Statement st : stats) { - stat_vars.add(splitVaribles(st, " " + indent)); - } + Map vars = varproc.getLVT().getVars(stat.getParentSequenceStat()); - for (int x = 0; x < stats.size(); x++) { - switch (stats.get(x).type) { - case Statement.TYPE_DO: { - DoStatement dost = (DoStatement)stats.get(x); - VarVersionPair init = extractVar(dost.getInitExprent()); - if (init != null && (dost.getLooptype() == DoStatement.LOOP_FOR || dost.getLooptype()== DoStatement.LOOP_FOREACH)) { - if (safeToRename(init, stats, stat_vars, x)) { - VarVersionPair newIndex = new VarVersionPair(counters.getCounterAndIncrement(CounterContainer.VAR_COUNTER), 0); - remapVar(init, newIndex, stats.get(x), stat_vars.get(x)); - } - } - break; - } + if (stat.getExprents() == null) { + for (Object obj : stat.getSequentialObjects()) { + if (obj instanceof Statement) { + setupLVTs((Statement)obj); } - } - - for (Map var : stat_vars) { - for (Entry entry : var.entrySet()) { - if (!vars.containsKey(entry.getKey())) { - vars.put(entry.getKey(), entry.getValue()); - if (seqStat != null) { - scopedVVPs.get(seqStat).add(entry.getKey()); - } - } + else if (obj instanceof Exprent) { + setupLVTs((Exprent)obj, vars); } } } else { - for (Exprent e : stat.getExprents()) { - splitExprent(e, vars, indent); - } - } - - if (stat.type == Statement.TYPE_DO) { - DoStatement dost = (DoStatement)stat; - if (dost.getLooptype() == DoStatement.LOOP_DOWHILE) { - splitExprent(dost.getConditionExprent(), vars, indent); + for (Exprent exp : stat.getExprents()) { + setupLVTs(exp, vars); } } - - //for (Map.Entry entry : vars.entrySet()) { - // System.out.println(indent + " " + (entry.getValue() ? "ass " : "ref ") + entry.getKey()); - //} - - return vars; } - private static void splitExprent(Exprent e, Map map, String indent) { - if (e == null) + private void setupLVTs(Exprent exprent, Map lvts) { + if (exprent == null) { return; - if (e.type == Exprent.EXPRENT_ASSIGNMENT && ((AssignmentExprent)e).getLeft().type == Exprent.EXPRENT_VAR) { - VarVersionPair var = new VarVersionPair((VarExprent)((AssignmentExprent)e).getLeft()); - if (!map.containsKey(var)) { - map.put(var, true); - } - splitExprent(((AssignmentExprent)e).getRight(), map, indent); } - else { - for (VarVersionPair var : e.getAllVariables()) { - if (!map.containsKey(var)) { - map.put(var, false); - } - } - } - } - - private static VarVersionPair extractVar(Exprent exp) { - if (exp == null) { - return null; - } - if (exp.type == Exprent.EXPRENT_ASSIGNMENT && ((AssignmentExprent)exp).getLeft().type == Exprent.EXPRENT_VAR) { - return new VarVersionPair((VarExprent)((AssignmentExprent)exp).getLeft()); - } - else if (exp.type == Exprent.EXPRENT_VAR) { - return new VarVersionPair((VarExprent)exp); - } - return null; - } + List lst = exprent.getAllExprents(true); + lst.add(exprent); - private static boolean safeToRename(VarVersionPair var, List stats, List> list, int index) { - for (int x = index + 1; x < list.size(); x++) { - Map map = list.get(x); - if (map.containsKey(var)) { - return map.get(var); + for (Exprent expr : lst) { + if (expr.type == Exprent.EXPRENT_VAR) { + VarExprent var = (VarExprent)expr; + int index = varproc.getRemapped(var.getIndex()); + LVTVariable lvt = lvts.get(index); + if (lvt != null) { + var.setLVT(lvt); + } } } - return false; - } - - private void remapVar(VarVersionPair from, VarVersionPair to, Statement stat, Map stats) { - remapVar(from, to, stat); - varproc.copyVarInfo(from, to); - stats.put(to, true); - stats.remove(from); } - private static void remapVar(VarVersionPair from, VarVersionPair to, Statement stat) { - if (stat == null) { - return; - } + private void mergeVars(Statement stat) { if (stat.getExprents() == null) { for (Object obj : stat.getSequentialObjects()) { if (obj instanceof Statement) { - remapVar(from, to, (Statement)obj); + mergeVars((Statement)obj); } - else if (obj instanceof Exprent) { - remapVar(from, to, (Exprent)obj); + else if (obj instanceof AssignmentExprent) { + remapVar(checkAssignment((AssignmentExprent)obj), stat.getParent()); } } } else { for (Exprent exp : stat.getExprents()) { - remapVar(from, to, exp); + if (exp instanceof AssignmentExprent) { + remapVar(checkAssignment((AssignmentExprent)exp), stat.getParent()); + } } } } - private static void remapVar(VarVersionPair from, VarVersionPair to, Exprent exprent) { - if (exprent == null) { - return; + private Map.Entry checkAssignment(AssignmentExprent exp) { + if (exp.getLeft().type != Exprent.EXPRENT_VAR) { + return null; } - List lst = exprent.getAllExprents(true); - lst.add(exprent); - for (Exprent expr : lst) { - if (expr.type == Exprent.EXPRENT_VAR) { - VarExprent var = (VarExprent)expr; - if (var.getIndex() == from.var && var.getVersion() == from.version) { - var.setIndex(to.var); - var.setVersion(to.version); + VarExprent left = (VarExprent)exp.getLeft(); + VarExprent right = null; + int index = varproc.getRemapped(left.getIndex()); + + for (Exprent e : exp.getRight().getAllExprents(true)) { + if (e.type == Exprent.EXPRENT_VAR) { + if (varproc.getRemapped(((VarExprent)e).getIndex()) == index) { + right = (VarExprent)e; + break; } } } + + if (right == null) { + return null; + } + + return new SimpleImmutableEntry(new VarVersionPair(left), new VarVersionPair(right)); } - private void setupLVTs(Statement stat) { - if (stat == null || varproc.getLVT() == null) { + private static void remapVar(Entry remap, Statement stat) { + if (remap == null || stat == null) { return; } - Map vars = varproc.getLVT().getVars(stat.getParentSequenceStat()); - List vvps = scopedVVPs.get(stat.getBasichead()); - if (stat.getExprents() == null) { for (Object obj : stat.getSequentialObjects()) { if (obj instanceof Statement) { - setupLVTs((Statement)obj); + remapVar(remap, (Statement)obj); } else if (obj instanceof Exprent) { - setupLVTs((Exprent)obj, vars); + remapVar(remap.getKey(), remap.getValue(), (Exprent)obj); } } } else { for (Exprent exp : stat.getExprents()) { - setupLVTs(exp, vars); + remapVar(remap.getKey(), remap.getValue(), exp); } } } - private void setupLVTs(Exprent exprent, Map lvts) { + private static void remapVar(VarVersionPair from, VarVersionPair to, Exprent exprent) { if (exprent == null) { return; } @@ -621,12 +503,11 @@ private void setupLVTs(Exprent exprent, Map lvts) { for (Exprent expr : lst) { if (expr.type == Exprent.EXPRENT_VAR) { VarExprent var = (VarExprent)expr; - int index = varproc.getRemapped(var.getIndex()); - LVTVariable lvt = lvts.get(index); - if (lvt != null) { - var.setLVT(lvt); + if (var.getIndex() == from.var && var.getVersion() == from.version) { + var.setIndex(to.var); + var.setVersion(to.version); } } } } -} +} \ No newline at end of file From 09209954680f314ed735818b7f22ca1e81080509 Mon Sep 17 00:00:00 2001 From: cpw Date: Sat, 12 Sep 2015 00:48:02 -0400 Subject: [PATCH 21/73] need to match both start and end, to avoid weirdness sometimes --- .../modules/decompiler/vars/LVTVariable.java | 7 ++++--- .../decompiler/vars/LocalVariableTable.java | 17 +++++------------ .../modules/decompiler/vars/StartEndPair.java | 18 ++++++++++++++++++ 3 files changed, 27 insertions(+), 15 deletions(-) create mode 100644 src/org/jetbrains/java/decompiler/modules/decompiler/vars/StartEndPair.java diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LVTVariable.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LVTVariable.java index bc7ba23..59dee69 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LVTVariable.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LVTVariable.java @@ -50,11 +50,12 @@ public int hashCode() { return index * 31 + end; } - public void addTo(Map> endpoints) { - Set ends = endpoints.get(this.end); + public void addTo(Map> endpoints) { + StartEndPair sepair = new StartEndPair(this.start, this.end); + Set ends = endpoints.get(sepair); if (ends == null) { ends = new HashSet(); - endpoints.put(this.end, ends); + endpoints.put(sepair, ends); } ends.add(this); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java index f7e1858..1de7181 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java @@ -1,31 +1,23 @@ package org.jetbrains.java.decompiler.modules.decompiler.vars; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.BitSet; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Set; -import java.util.Stack; -import org.jetbrains.java.decompiler.code.Instruction; -import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.main.rels.MethodProcessorRunnable; -import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; -import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; public class LocalVariableTable { - private Map> endpoints; + private Map> endpoints; private ArrayList allLVT; private Map> mapLVT; public LocalVariableTable(int len) { - endpoints = new HashMap>(len); + endpoints = new HashMap>(len); allLVT = new ArrayList(len); } @@ -106,9 +98,10 @@ public Map getVars(SequenceStatement sequenceStatement) { } //System.out.println(indent + stat.getClass().getSimpleName() + " (" + start +", " + end + ")"); + StartEndPair sepair = new StartEndPair(start, end); Map ret = new HashMap(); - if (endpoints.containsKey(end)) { - for (LVTVariable lvt : endpoints.get(end)) { + if (endpoints.containsKey(sepair)) { + for (LVTVariable lvt : endpoints.get(sepair)) { ret.put(lvt.index,lvt); } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/StartEndPair.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/StartEndPair.java new file mode 100644 index 0000000..23a3929 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/StartEndPair.java @@ -0,0 +1,18 @@ +package org.jetbrains.java.decompiler.modules.decompiler.vars; + +public class StartEndPair { + public final int start; + public final int end; + public StartEndPair(int start, int end) { + this.start = start; + this.end = end; + } + @Override + public boolean equals(Object obj) { + return ((StartEndPair)obj).start == start && ((StartEndPair)obj).end == end; + } + @Override + public int hashCode() { + return start * 31 + end; + } +} From fcb5e35a68f7155018b1330846e9fe8222f9e39d Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Fri, 11 Sep 2015 21:50:11 -0700 Subject: [PATCH 22/73] Better remapping of assignment merges. --- .../decompiler/vars/VarDefinitionHelper.java | 56 +++++++++++-------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java index 5a276cd..2436d6d 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java @@ -427,51 +427,61 @@ private void setupLVTs(Exprent exprent, Map lvts) { } private void mergeVars(Statement stat) { + Map remaps = new HashMap(); + mergeVars(stat, remaps); + if (!remaps.isEmpty()) { + for (Entry entry : remaps.entrySet()) { + VarVersionPair end = entry.getKey(); + while (remaps.containsKey(end)) { + end = remaps.get(end); + } + remaps.put(entry.getKey(), end); + } + remapVar(remaps, stat); + } + } + private void mergeVars(Statement stat, Map remaps) { if (stat.getExprents() == null) { for (Object obj : stat.getSequentialObjects()) { if (obj instanceof Statement) { - mergeVars((Statement)obj); + mergeVars((Statement)obj, remaps); } else if (obj instanceof AssignmentExprent) { - remapVar(checkAssignment((AssignmentExprent)obj), stat.getParent()); + checkAssignment((AssignmentExprent)obj, remaps); } } } else { for (Exprent exp : stat.getExprents()) { if (exp instanceof AssignmentExprent) { - remapVar(checkAssignment((AssignmentExprent)exp), stat.getParent()); + checkAssignment((AssignmentExprent)exp, remaps); } } } } - private Map.Entry checkAssignment(AssignmentExprent exp) { + private void checkAssignment(AssignmentExprent exp, Map remaps) { if (exp.getLeft().type != Exprent.EXPRENT_VAR) { - return null; + return; } VarExprent left = (VarExprent)exp.getLeft(); - VarExprent right = null; int index = varproc.getRemapped(left.getIndex()); for (Exprent e : exp.getRight().getAllExprents(true)) { if (e.type == Exprent.EXPRENT_VAR) { - if (varproc.getRemapped(((VarExprent)e).getIndex()) == index) { - right = (VarExprent)e; - break; + VarExprent right = (VarExprent)e; + if (varproc.getRemapped(right.getIndex()) == index) { + if (!left.equals(right) && left.getIndex() > right.getIndex()) { + remaps.put(new VarVersionPair(left), new VarVersionPair(right)); + } + return; } } } - - if (right == null) { - return null; - } - - return new SimpleImmutableEntry(new VarVersionPair(left), new VarVersionPair(right)); } - private static void remapVar(Entry remap, Statement stat) { + private static void remapVar(Map remap, Statement stat) { if (remap == null || stat == null) { return; } @@ -482,18 +492,18 @@ private static void remapVar(Entry remap, Statem remapVar(remap, (Statement)obj); } else if (obj instanceof Exprent) { - remapVar(remap.getKey(), remap.getValue(), (Exprent)obj); + remapVar(remap, (Exprent)obj); } } } else { for (Exprent exp : stat.getExprents()) { - remapVar(remap.getKey(), remap.getValue(), exp); + remapVar(remap, exp); } } } - private static void remapVar(VarVersionPair from, VarVersionPair to, Exprent exprent) { + private static void remapVar(Map remap, Exprent exprent) { if (exprent == null) { return; } @@ -503,9 +513,11 @@ private static void remapVar(VarVersionPair from, VarVersionPair to, Exprent exp for (Exprent expr : lst) { if (expr.type == Exprent.EXPRENT_VAR) { VarExprent var = (VarExprent)expr; - if (var.getIndex() == from.var && var.getVersion() == from.version) { - var.setIndex(to.var); - var.setVersion(to.version); + VarVersionPair old = new VarVersionPair(var); + VarVersionPair new_ = remap.get(old); + if (new_ != null) { + var.setIndex(new_.var); + var.setVersion(new_.version); } } } From ab0b5409d7cacdc6d9c208de4657377a4a259c05 Mon Sep 17 00:00:00 2001 From: cpw Date: Sun, 13 Sep 2015 18:25:07 -0400 Subject: [PATCH 23/73] PPMM test for lex --- test/org/jetbrains/java/decompiler/LVTTest.java | 9 ++++++--- testData/classes/pkg/TestPPMM.class | Bin 0 -> 371 bytes testData/src/pkg/TestPPMM.java | 11 +++++++++++ 3 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 testData/classes/pkg/TestPPMM.class create mode 100644 testData/src/pkg/TestPPMM.java diff --git a/test/org/jetbrains/java/decompiler/LVTTest.java b/test/org/jetbrains/java/decompiler/LVTTest.java index f4d869c..d2a8f1c 100644 --- a/test/org/jetbrains/java/decompiler/LVTTest.java +++ b/test/org/jetbrains/java/decompiler/LVTTest.java @@ -35,7 +35,10 @@ public void setUp() throws IOException { super.setUp(); fixture.cleanup = false; } - @Test public void testMatchLM() { doTest("pkg/TestLexManosLVT"); } - @Test public void testMatch1() { doTest("pkg/TestLVT"); } - @Test public void testMatch2() { doTest("pkg/TestLVTScoping"); } + @Test public void testMatch1() { doTest("pkg/TestPPMM"); } +// @Test public void testMatchLM() { doTest("pkg/TestLexManosLVT"); } +// @Test public void testMatch1() { doTest("pkg/TestLVT"); } +// @Test public void testMatch2() { doTest("pkg/TestLVTScoping"); } +// @Test public void testMCWorld() { doTest("net/minecraft/world/World"); } +// @Test public void testMCGuiCmdBlock() { doTest("net/minecraft/client/gui/GuiCommandBlock"); } } diff --git a/testData/classes/pkg/TestPPMM.class b/testData/classes/pkg/TestPPMM.class new file mode 100644 index 0000000000000000000000000000000000000000..e493a99dbb742f1f20c3cb134d7366db47c5d4b4 GIT binary patch literal 371 zcmYjL%}&BV7@V&yEtFyr6A#|JsRx^Qm3T2RF)2zS!Sl9maIv;cDL#QOeh z`a)nI9Zv<$W$~bW*ysdk!4-tD$@R_hInmWbC7Bis;v!YqR8^+QH$|s@Gz-B_{D0@U zz$#0DD86ffe^)H4R9~4!ruXaYv@wN4&;rc^7Jpn#^q57A!q_@_NBeEn_K8g!By@?v zdVsOVj3&0^duA4Ep*?&R+1@{y1CCOgLoU%no2QEoCH$52{+hm#wzwcfpQ3_Y@+=H~ E0NOq`X8-^I literal 0 HcmV?d00001 diff --git a/testData/src/pkg/TestPPMM.java b/testData/src/pkg/TestPPMM.java new file mode 100644 index 0000000..02eecf4 --- /dev/null +++ b/testData/src/pkg/TestPPMM.java @@ -0,0 +1,11 @@ +package pkg; + +public class TestPPMM { + public void pp() { + int a = 0; + a++; + a++; + a++; + a++; + } +} \ No newline at end of file From 78afb4580567df9f7d1e6245dc9735eba714a3b2 Mon Sep 17 00:00:00 2001 From: cpw Date: Sun, 13 Sep 2015 18:26:04 -0400 Subject: [PATCH 24/73] Change begin/end detection. Less derpy. --- .../modules/decompiler/stats/DoStatement.java | 6 +++++ .../modules/decompiler/stats/IfStatement.java | 15 ++++++++----- .../decompiler/stats/RootStatement.java | 6 +++++ .../decompiler/stats/SequenceStatement.java | 6 +++++ .../modules/decompiler/stats/Statement.java | 22 ++++++++++++------- .../decompiler/stats/SwitchStatement.java | 12 ++++++++++ .../decompiler/vars/LocalVariableTable.java | 17 +++++--------- .../modules/decompiler/vars/StartEndPair.java | 15 +++++++++++++ .../decompiler/vars/VarDefinitionHelper.java | 14 +++++++----- 9 files changed, 82 insertions(+), 31 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java index 8359284..0768912 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java @@ -21,6 +21,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.vars.StartEndPair; import java.util.ArrayList; import java.util.List; @@ -241,4 +242,9 @@ public int getLooptype() { public void setLooptype(int looptype) { this.looptype = looptype; } + + @Override + public StartEndPair getStartEndRange() { + return super.getStartEndRange(); + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java index 8be0dcc..f216b73 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java @@ -22,6 +22,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.IfExprent; +import org.jetbrains.java.decompiler.modules.decompiler.vars.StartEndPair; import org.jetbrains.java.decompiler.struct.match.IMatchable; import org.jetbrains.java.decompiler.struct.match.MatchEngine; import org.jetbrains.java.decompiler.struct.match.MatchNode; @@ -425,18 +426,18 @@ public StatEdge getIfEdge() { public StatEdge getElseEdge() { return elseedge; } - + // ***************************************************************************** // IMatchable implementation // ***************************************************************************** - + public IMatchable findObject(MatchNode matchNode, int index) { IMatchable object = super.findObject(matchNode, index); if(object != null) { return object; } - + if(matchNode.getType() == MatchNode.MATCHNODE_EXPRENT) { String position = (String)matchNode.getRuleValue(MatchProperties.EXPRENT_POSITION); if("head".equals(position)) { @@ -459,8 +460,12 @@ public boolean match(MatchNode matchNode, MatchEngine engine) { return false; } } - + return true; } - + + @Override + public StartEndPair getStartEndRange() { + return StartEndPair.join(super.getStartEndRange(),ifstat != null ? ifstat.getStartEndRange() : null,elsestat != null ? elsestat.getStartEndRange(): null); + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/RootStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/RootStatement.java index 7193f9a..0a1e798 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/RootStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/RootStatement.java @@ -18,6 +18,7 @@ import org.jetbrains.java.decompiler.main.TextBuffer; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.vars.StartEndPair; public class RootStatement extends Statement { @@ -46,4 +47,9 @@ public DummyExitStatement getDummyExit() { public void setDummyExit(DummyExitStatement dummyExit) { this.dummyExit = dummyExit; } + + @Override + public StartEndPair getStartEndRange() { + return first.getStartEndRange(); + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java index 7d2ee43..b3785fb 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java @@ -20,6 +20,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.DecHelper; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; +import org.jetbrains.java.decompiler.modules.decompiler.vars.StartEndPair; import java.util.Arrays; import java.util.List; @@ -137,4 +138,9 @@ public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { public Statement getSimpleCopy() { return new SequenceStatement(); } + + @Override + public StartEndPair getStartEndRange() { + return super.getStartEndRange(); + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java index c126050..806c4d5 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java @@ -24,6 +24,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; import org.jetbrains.java.decompiler.modules.decompiler.StrongConnectivityHelper; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.vars.StartEndPair; import org.jetbrains.java.decompiler.struct.match.IMatchable; import org.jetbrains.java.decompiler.struct.match.MatchEngine; import org.jetbrains.java.decompiler.struct.match.MatchNode; @@ -938,12 +939,17 @@ public boolean match(MatchNode matchNode, MatchEngine engine) { return true; } - private SequenceStatement parentSeqStat; -public SequenceStatement getParentSequenceStat() { - if (parentSeqStat == null) { - parentSeqStat = (getParent()!=null && getParent().type == TYPE_SEQUENCE) ? (SequenceStatement)getParent() : new SequenceStatement(Arrays.asList(this)); - } - return parentSeqStat; -} - + private StartEndPair endpoints; + public StartEndPair getStartEndRange() { + if (endpoints == null) { + int start = Integer.MAX_VALUE; + int end = Integer.MIN_VALUE; + for (Statement st : getStats()) { + start = Math.min(start, st.getBasichead().getBlock().getStartInstruction()); + end = Math.max(end, st.getBasichead().getBlock().getEndInstruction()); + } + endpoints = new StartEndPair(start,end); + } + return endpoints; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java index 5916490..8d7127c 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java @@ -27,6 +27,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.SwitchExprent; +import org.jetbrains.java.decompiler.modules.decompiler.vars.StartEndPair; import org.jetbrains.java.decompiler.struct.gen.VarType; import java.util.*; @@ -365,4 +366,15 @@ public StatEdge getDefault_edge() { public List> getCaseValues() { return caseValues; } + + @Override + public StartEndPair getStartEndRange() { + StartEndPair[] sepairs = new StartEndPair[caseStatements.size() + 1]; + int i = 0; + sepairs[i++] = super.getStartEndRange(); + for (Statement st : caseStatements) { + sepairs[i++] = st.getStartEndRange(); + } + return StartEndPair.join(sepairs); + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java index 1de7181..fe45b11 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.BitSet; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -88,18 +89,10 @@ public List getCandidates(int index) { return getMapVarNames().get(index); } - public Map getVars(SequenceStatement sequenceStatement) { - if (sequenceStatement == null) return new HashMap(); - int start = Integer.MAX_VALUE; - int end = Integer.MIN_VALUE; - for (Statement st : sequenceStatement.getStats()) { - start = Math.min(start, st.getBasichead().getBlock().getStartInstruction()); - end = Math.max(end, st.getBasichead().getBlock().getEndInstruction()); - } - //System.out.println(indent + stat.getClass().getSimpleName() + " (" + start +", " + end + ")"); - - StartEndPair sepair = new StartEndPair(start, end); - Map ret = new HashMap(); + public Map getVars(Statement statement) { + HashMap ret = new HashMap(); + if (statement == null) return ret; + StartEndPair sepair = statement.getStartEndRange(); if (endpoints.containsKey(sepair)) { for (LVTVariable lvt : endpoints.get(sepair)) { ret.put(lvt.index,lvt); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/StartEndPair.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/StartEndPair.java index 23a3929..32c8799 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/StartEndPair.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/StartEndPair.java @@ -15,4 +15,19 @@ public boolean equals(Object obj) { public int hashCode() { return start * 31 + end; } + @Override + public String toString() { + return String.format("%d->%d",start,end); + } + + public static StartEndPair join(StartEndPair... pairs) { + int start = Integer.MAX_VALUE; + int end = Integer.MIN_VALUE; + for (StartEndPair pair : pairs) { + if (pair == null) continue; + start = Math.min(start, pair.start); + end = Math.max(end, pair.end); + } + return new StartEndPair(start, end); + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java index 2436d6d..56061fe 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java @@ -127,13 +127,13 @@ else if (st.type == Statement.TYPE_TRYCATCH) { public void setVarDefinitions() { VarNamesCollector vc = DecompilerContext.getVarNamesCollector(); - Map> trackingMap = new HashMap>(); + Map> trackingMap = new HashMap>(); for (Entry en : mapVarDefStatements.entrySet()) { Statement stat = en.getValue(); - if (!trackingMap.containsKey(stat.getParentSequenceStat())) { - trackingMap.put(stat.getParentSequenceStat(), new HashMap()); + if (!trackingMap.containsKey(stat)) { + trackingMap.put(stat, new HashMap()); } - Map scopedMap = trackingMap.get(stat.getParentSequenceStat()); + Map scopedMap = trackingMap.get(stat); Integer index = en.getKey(); int newindex = varproc.getRemapped(index); setupLVTs(stat); @@ -221,7 +221,7 @@ else if (first.getExprents() == null) { var.setDefinition(true); if (varproc.getLVT() != null) { - Map vars = varproc.getLVT().getVars(stat.getParentSequenceStat()); + Map vars = varproc.getLVT().getVars(stat); if (vars.containsKey(var.getIndex())) { var.setLVT(vars.get(var.getIndex())); } @@ -388,7 +388,7 @@ private void setupLVTs(Statement stat) { return; } - Map vars = varproc.getLVT().getVars(stat.getParentSequenceStat()); + Map vars = varproc.getLVT().getVars(stat); if (stat.getExprents() == null) { for (Object obj : stat.getSequentialObjects()) { @@ -421,6 +421,8 @@ private void setupLVTs(Exprent exprent, Map lvts) { LVTVariable lvt = lvts.get(index); if (lvt != null) { var.setLVT(lvt); + } else { + System.currentTimeMillis(); } } } From 7e027aef8ba2caaee9f3e93412274b7073b8f6a7 Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Sun, 13 Sep 2015 17:49:00 -0700 Subject: [PATCH 25/73] Fixup NPE from PPMM helper, and expand PPMM test to include more edge cases. --- .../modules/decompiler/PPandMMHelper.java | 15 +++- .../modules/decompiler/exps/ConstExprent.java | 5 ++ testData/classes/pkg/TestPPMM.class | Bin 371 -> 936 bytes testData/results/TestPPMM.dec | 70 ++++++++++++++++++ testData/src/pkg/TestPPMM.java | 69 +++++++++++++++-- 5 files changed, 150 insertions(+), 9 deletions(-) create mode 100644 testData/results/TestPPMM.dec diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java index d93bad6..f39946d 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java @@ -34,6 +34,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Map.Entry; public class PPandMMHelper { @@ -183,13 +184,23 @@ private boolean varsEqual(Exprent e1, Exprent e2) { VarExprent v1 = (VarExprent)e1; VarExprent v2 = (VarExprent)e2; - return varProc.getRemapped(v1.getIndex()) == varProc.getRemapped(v2.getIndex()) - && InterpreterUtil.equalObjects(v1.getVarType(), v2.getVarType()); + return varProc.getRemapped(v1.getIndex()) == varProc.getRemapped(v2.getIndex()); + //Probably should up this to checking if the types are in the same family, Like byte == int == long + //&& InterpreterUtil.equalObjects(v1.getVarType(), v2.getVarType()); } private void updateVersions(DirectGraph graph) { if (remaps.isEmpty()) return; + + for (Entry remap : remaps.entrySet()) { + VarVersionPair target = remap.getValue(); + while (remaps.containsKey(target)) { + target = remaps.get(target); + } + remaps.put(remap.getKey(), target); + } + graph.iterateExprents(new DirectGraph.ExprentIterator() { @Override public int processExprent(Exprent exprent) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java index 312f6c8..3398924 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java @@ -416,6 +416,11 @@ public void getBytecodeRange(BitSet values) { measureBytecode(values); } + @Override + public String toString() { + return "const(" + toJava(0, new BytecodeMappingTracer()) + ")"; + } + // ***************************************************************************** // IMatchable implementation // ***************************************************************************** diff --git a/testData/classes/pkg/TestPPMM.class b/testData/classes/pkg/TestPPMM.class index e493a99dbb742f1f20c3cb134d7366db47c5d4b4..38580c978dbdec1baf1fe2a0d4c7bbb11e2d2a11 100644 GIT binary patch literal 936 zcmb`F&rZTX5XQfuMYI+{RQ!X67%%9Bg9i^1FCGjDL=J@ef)$Gt8flCVzzccOc<=#y zDDhiMiqR%=pxMs1JJb2?bTS{GZ|?w3v2Ve|js*j|DeR?C5G+;Pfg4;1jM8ySU|tSy z96_!&?5KWAJ-DjfcbeKT7Tmcb!CK9~@0Od+C}=e5^>b#rzAq5Px5|_6yV~b@w4TSk z$@ndW0WFMZVTd9Dx23ASTApkqv1DTld4c`&5<#}79#y%o2Ho;?yXSNQ9P%$r{uN+B zbRunvrz5IRlP52bXKn<`w07vk3P0Dr)hOYNrv!Z&qcRqW)+{w2hNr0sqzOwiiLF=! zBohyrjAgp!sU*7y$a)01n)akeh z`a)nI9Zv<$W$~bW*ysdk!4-tD$@R_hInmWbC7Bis;v!YqR8^+QH$|s@Gz-B_{D0@U zz$#0DD86ffe^)H4R9~4!ruXaYv@wN4&;rc^7Jpn#^q57A!q_@_NBeEn_K8g!By@?v zdVsOVj3&0^duA4Ep*?&R+1@{y1CCOgLoU%no2QEoCH$52{+hm#wzwcfpQ3_Y@+=H~ E0NOq`X8-^I diff --git a/testData/results/TestPPMM.dec b/testData/results/TestPPMM.dec new file mode 100644 index 0000000..ba3a5fc --- /dev/null +++ b/testData/results/TestPPMM.dec @@ -0,0 +1,70 @@ +package pkg; + +public class TestPPMM { + public void ipp() { + int a = 0; + ++a; + ++a; + ++a; + ++a; + } + + public void ppi() { + int a = 0; + ++a; + ++a; + ++a; + ++a; + } + + public void imm() { + int a = 0; + --a; + --a; + --a; + --a; + } + + public void mmi() { + int a = 0; + --a; + --a; + --a; + --a; + } + + public void ippf() { + int a = 0; + t(a++); + t(a++); + t(a++); + t(a++); + } + + public void ppif() { + int a = 0; + t(++a); + t(++a); + t(++a); + t(++a); + } + + public void immf() { + int a = 0; + t(a--); + t(a--); + t(a--); + t(a--); + } + + public void mmif() { + int a = 0; + t(--a); + t(--a); + t(--a); + t(--a); + } + + private static void t(int x) { + } +} diff --git a/testData/src/pkg/TestPPMM.java b/testData/src/pkg/TestPPMM.java index 02eecf4..21c7f2e 100644 --- a/testData/src/pkg/TestPPMM.java +++ b/testData/src/pkg/TestPPMM.java @@ -1,11 +1,66 @@ package pkg; public class TestPPMM { - public void pp() { - int a = 0; - a++; - a++; - a++; - a++; - } + // Bytecode wise ipp and ppi are identical when not using the intermediate value. + // We keep these seperate tests just to see the bytecode. + public void ipp() { + int a = 0; + a++; + a++; + a++; + a++; + } + public void ppi() { + int a = 0; + ++a; + ++a; + ++a; + ++a; + } + public void imm() { + int a = 0; + a--; + a--; + a--; + a--; + } + public void mmi() { + int a = 0; + --a; + --a; + --a; + --a; + } + + // These versions actually use the intermediate value + public void ippf() { + int a = 0; + t(a++); + t(a++); + t(a++); + t(a++); + } + public void ppif() { + int a = 0; + t(++a); + t(++a); + t(++a); + t(++a); + } + public void immf() { + int a = 0; + t(a--); + t(a--); + t(a--); + t(a--); + } + public void mmif() { + int a = 0; + t(--a); + t(--a); + t(--a); + t(--a); + } + private static void t(int x){ + } } \ No newline at end of file From 613a1873954a310ee5ee18be6db5790e6ec74ba0 Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Sun, 13 Sep 2015 18:19:20 -0700 Subject: [PATCH 26/73] Prevent infinite loops in PPMM. --- .../java/decompiler/modules/decompiler/PPandMMHelper.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java index f39946d..db70f64 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java @@ -157,7 +157,11 @@ private Exprent processExprentRecursive(Exprent exprent) { exprentReplaced = true; if (!left.equals(econd)) { - remaps.put(new VarVersionPair((VarExprent)left), new VarVersionPair((VarExprent)econd)); + VarVersionPair _old = new VarVersionPair((VarExprent)left); + VarVersionPair _new = new VarVersionPair((VarExprent)econd); + if (_old.var > _new.var) { // Try and prevent infinite loops here by only merging 'down' + remaps.put(_old, _new); + } } return ret; } From 5f3e0ca392561ae4c3a9a4ec50eeb1a1aacf72b5 Mon Sep 17 00:00:00 2001 From: cpw Date: Sun, 13 Sep 2015 21:52:45 -0400 Subject: [PATCH 27/73] Merge in and use alternative code for ppmm --- .../modules/decompiler/PPandMMHelper.java | 6 +- .../modules/decompiler/exps/ConstExprent.java | 5 ++ testData/classes/pkg/TestPPMM.class | Bin 371 -> 936 bytes testData/results/TestPPMM.dec | 70 ++++++++++++++++++ testData/src/pkg/TestPPMM.java | 69 +++++++++++++++-- 5 files changed, 141 insertions(+), 9 deletions(-) create mode 100644 testData/results/TestPPMM.dec diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java index d93bad6..cbde16f 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java @@ -183,8 +183,10 @@ private boolean varsEqual(Exprent e1, Exprent e2) { VarExprent v1 = (VarExprent)e1; VarExprent v2 = (VarExprent)e2; - return varProc.getRemapped(v1.getIndex()) == varProc.getRemapped(v2.getIndex()) - && InterpreterUtil.equalObjects(v1.getVarType(), v2.getVarType()); + if (remaps.containsKey(new VarVersionPair(v1.getIndex(),v1.getVersion())) || remaps.containsKey(new VarVersionPair(v2.getIndex(),v2.getVersion()))) { + return false; + } + return varProc.getRemapped(v1.getIndex()) == varProc.getRemapped(v2.getIndex()); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java index 312f6c8..3398924 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java @@ -416,6 +416,11 @@ public void getBytecodeRange(BitSet values) { measureBytecode(values); } + @Override + public String toString() { + return "const(" + toJava(0, new BytecodeMappingTracer()) + ")"; + } + // ***************************************************************************** // IMatchable implementation // ***************************************************************************** diff --git a/testData/classes/pkg/TestPPMM.class b/testData/classes/pkg/TestPPMM.class index e493a99dbb742f1f20c3cb134d7366db47c5d4b4..38580c978dbdec1baf1fe2a0d4c7bbb11e2d2a11 100644 GIT binary patch literal 936 zcmb`F&rZTX5XQfuMYI+{RQ!X67%%9Bg9i^1FCGjDL=J@ef)$Gt8flCVzzccOc<=#y zDDhiMiqR%=pxMs1JJb2?bTS{GZ|?w3v2Ve|js*j|DeR?C5G+;Pfg4;1jM8ySU|tSy z96_!&?5KWAJ-DjfcbeKT7Tmcb!CK9~@0Od+C}=e5^>b#rzAq5Px5|_6yV~b@w4TSk z$@ndW0WFMZVTd9Dx23ASTApkqv1DTld4c`&5<#}79#y%o2Ho;?yXSNQ9P%$r{uN+B zbRunvrz5IRlP52bXKn<`w07vk3P0Dr)hOYNrv!Z&qcRqW)+{w2hNr0sqzOwiiLF=! zBohyrjAgp!sU*7y$a)01n)akeh z`a)nI9Zv<$W$~bW*ysdk!4-tD$@R_hInmWbC7Bis;v!YqR8^+QH$|s@Gz-B_{D0@U zz$#0DD86ffe^)H4R9~4!ruXaYv@wN4&;rc^7Jpn#^q57A!q_@_NBeEn_K8g!By@?v zdVsOVj3&0^duA4Ep*?&R+1@{y1CCOgLoU%no2QEoCH$52{+hm#wzwcfpQ3_Y@+=H~ E0NOq`X8-^I diff --git a/testData/results/TestPPMM.dec b/testData/results/TestPPMM.dec new file mode 100644 index 0000000..ba3a5fc --- /dev/null +++ b/testData/results/TestPPMM.dec @@ -0,0 +1,70 @@ +package pkg; + +public class TestPPMM { + public void ipp() { + int a = 0; + ++a; + ++a; + ++a; + ++a; + } + + public void ppi() { + int a = 0; + ++a; + ++a; + ++a; + ++a; + } + + public void imm() { + int a = 0; + --a; + --a; + --a; + --a; + } + + public void mmi() { + int a = 0; + --a; + --a; + --a; + --a; + } + + public void ippf() { + int a = 0; + t(a++); + t(a++); + t(a++); + t(a++); + } + + public void ppif() { + int a = 0; + t(++a); + t(++a); + t(++a); + t(++a); + } + + public void immf() { + int a = 0; + t(a--); + t(a--); + t(a--); + t(a--); + } + + public void mmif() { + int a = 0; + t(--a); + t(--a); + t(--a); + t(--a); + } + + private static void t(int x) { + } +} diff --git a/testData/src/pkg/TestPPMM.java b/testData/src/pkg/TestPPMM.java index 02eecf4..21c7f2e 100644 --- a/testData/src/pkg/TestPPMM.java +++ b/testData/src/pkg/TestPPMM.java @@ -1,11 +1,66 @@ package pkg; public class TestPPMM { - public void pp() { - int a = 0; - a++; - a++; - a++; - a++; - } + // Bytecode wise ipp and ppi are identical when not using the intermediate value. + // We keep these seperate tests just to see the bytecode. + public void ipp() { + int a = 0; + a++; + a++; + a++; + a++; + } + public void ppi() { + int a = 0; + ++a; + ++a; + ++a; + ++a; + } + public void imm() { + int a = 0; + a--; + a--; + a--; + a--; + } + public void mmi() { + int a = 0; + --a; + --a; + --a; + --a; + } + + // These versions actually use the intermediate value + public void ippf() { + int a = 0; + t(a++); + t(a++); + t(a++); + t(a++); + } + public void ppif() { + int a = 0; + t(++a); + t(++a); + t(++a); + t(++a); + } + public void immf() { + int a = 0; + t(a--); + t(a--); + t(a--); + t(a--); + } + public void mmif() { + int a = 0; + t(--a); + t(--a); + t(--a); + t(--a); + } + private static void t(int x){ + } } \ No newline at end of file From 58f09199efda8b5923200b57f1e7c22c63f61b4d Mon Sep 17 00:00:00 2001 From: cpw Date: Sun, 13 Sep 2015 22:34:26 -0400 Subject: [PATCH 28/73] Kill the remapping tracking entirely, and just do a full graph replace on contraction. Not as efficient, but guaranteed to work.. --- .../modules/decompiler/PPandMMHelper.java | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java index cbde16f..99d22bd 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java @@ -39,7 +39,7 @@ public class PPandMMHelper { private boolean exprentReplaced; private VarProcessor varProc; - private Map remaps = new HashMap(); + private DirectGraph dgraph; public PPandMMHelper(VarProcessor varProc) { this.varProc = varProc; @@ -48,7 +48,7 @@ public PPandMMHelper(VarProcessor varProc) { public boolean findPPandMM(RootStatement root) { FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); - DirectGraph dgraph = flatthelper.buildDirectGraph(root); + dgraph = flatthelper.buildDirectGraph(root); LinkedList stack = new LinkedList(); stack.add(dgraph.first); @@ -71,8 +71,6 @@ public boolean findPPandMM(RootStatement root) { stack.addAll(node.succs); } - updateVersions(dgraph); - return res; } @@ -99,7 +97,6 @@ private boolean processExprentList(List lst) { } private Exprent processExprentRecursive(Exprent exprent) { - boolean replaced = true; while (replaced) { replaced = false; @@ -147,7 +144,7 @@ private Exprent processExprentRecursive(Exprent exprent) { if (econst.type == Exprent.EXPRENT_CONST && ((ConstExprent)econst).hasValueOne()) { Exprent left = as.getLeft(); - VarType condtype = econd.getExprType(); + VarType condtype = left.getExprType(); if (exprsEqual(left, econd) && (midlayer == null || midlayer.equals(condtype))) { FunctionExprent ret = new FunctionExprent( func.getFuncType() == FunctionExprent.FUNCTION_ADD ? FunctionExprent.FUNCTION_PPI : FunctionExprent.FUNCTION_MMI, @@ -156,7 +153,7 @@ private Exprent processExprentRecursive(Exprent exprent) { exprentReplaced = true; if (!left.equals(econd)) { - remaps.put(new VarVersionPair((VarExprent)left), new VarVersionPair((VarExprent)econd)); + updateVersions(dgraph, new VarVersionPair((VarExprent) left), new VarVersionPair((VarExprent) econd)); } return ret; } @@ -183,15 +180,11 @@ private boolean varsEqual(Exprent e1, Exprent e2) { VarExprent v1 = (VarExprent)e1; VarExprent v2 = (VarExprent)e2; - if (remaps.containsKey(new VarVersionPair(v1.getIndex(),v1.getVersion())) || remaps.containsKey(new VarVersionPair(v2.getIndex(),v2.getVersion()))) { - return false; - } return varProc.getRemapped(v1.getIndex()) == varProc.getRemapped(v2.getIndex()); } - private void updateVersions(DirectGraph graph) { - if (remaps.isEmpty()) return; + private void updateVersions(DirectGraph graph, final VarVersionPair oldVVP, final VarVersionPair newVVP) { graph.iterateExprents(new DirectGraph.ExprentIterator() { @Override public int processExprent(Exprent exprent) { @@ -201,10 +194,9 @@ public int processExprent(Exprent exprent) { for (Exprent expr : lst) { if (expr.type == Exprent.EXPRENT_VAR) { VarExprent var = (VarExprent)expr; - VarVersionPair nvar = remaps.get(new VarVersionPair(var)); - if (nvar != null) { - var.setIndex(nvar.var); - var.setVersion(nvar.version); + if (var.getIndex() == oldVVP.var && var.getVersion() == oldVVP.version) { + var.setIndex(newVVP.var); + var.setVersion(newVVP.version); } } } From 38305edba97b6b5ae23d451ffb9d5dee9dc4bcf4 Mon Sep 17 00:00:00 2001 From: cpw Date: Mon, 14 Sep 2015 00:42:17 -0400 Subject: [PATCH 29/73] Types are merged properly now --- .../modules/decompiler/exps/VarExprent.java | 3 +++ .../modules/decompiler/vars/LVTVariable.java | 6 ++++++ .../decompiler/vars/VarDefinitionHelper.java | 11 +++++++++-- .../modules/decompiler/vars/VarProcessor.java | 4 ++++ .../decompiler/vars/VarVersionsProcessor.java | 3 +++ testData/classes/pkg/TestVarType.class | Bin 0 -> 375 bytes testData/src/pkg/TestVarType.java | 9 +++++++++ 7 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 testData/classes/pkg/TestVarType.class create mode 100644 testData/src/pkg/TestVarType.java diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java index 6c973af..3d5f03b 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java @@ -231,6 +231,9 @@ public boolean match(MatchNode matchNode, MatchEngine engine) { public void setLVT(LVTVariable lvt) { this.lvt = lvt; + if (processor != null) { + processor.setVarType(new VarVersionPair(this),lvt.getVarType()); + } } @Override diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LVTVariable.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LVTVariable.java index 59dee69..209d3e0 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LVTVariable.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LVTVariable.java @@ -5,6 +5,8 @@ import java.util.Map; import java.util.Set; +import org.jetbrains.java.decompiler.struct.gen.VarType; + public class LVTVariable implements Comparable { public static final Comparator INDEX_SORTER = new Comparator() { @Override @@ -80,4 +82,8 @@ public String getDesc() { public String getSig() { return sig; } + +public VarType getVarType() { + return new VarType(desc); +} } \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java index 56061fe..7abcec8 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java @@ -24,11 +24,13 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; +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.StructClass; import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; +import org.jetbrains.java.decompiler.struct.gen.VarType; import java.util.*; import java.util.Map.Entry; @@ -483,7 +485,7 @@ private void checkAssignment(AssignmentExprent exp, Map remap, Statement stat) { + private void remapVar(Map remap, Statement stat) { if (remap == null || stat == null) { return; } @@ -505,12 +507,13 @@ else if (obj instanceof Exprent) { } } - private static void remapVar(Map remap, Exprent exprent) { + private void remapVar(Map remap, Exprent exprent) { if (exprent == null) { return; } List lst = exprent.getAllExprents(true); lst.add(exprent); + Map mapExprentMinTypes = varproc.getVarVersions().getTypeProcessor().getMapExprentMinTypes(); for (Exprent expr : lst) { if (expr.type == Exprent.EXPRENT_VAR) { @@ -518,8 +521,12 @@ private static void remapVar(Map remap, Exprent VarVersionPair old = new VarVersionPair(var); VarVersionPair new_ = remap.get(old); if (new_ != null) { + VarType firstMinType = mapExprentMinTypes.get(old); + VarType secondMinType = mapExprentMinTypes.get(new_); var.setIndex(new_.var); var.setVersion(new_.version); + VarType type = VarType.getCommonSupertype(firstMinType, secondMinType); + mapExprentMinTypes.put(new_, type); } } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java index a8ad019..691797e 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java @@ -176,4 +176,8 @@ public void copyVarInfo(VarVersionPair from, VarVersionPair to) { setVarType(to, getVarType(from)); varVersions.getMapOriginalVarIndices().put(to.var, varVersions.getMapOriginalVarIndices().get(from.var)); } + + public VarVersionsProcessor getVarVersions() { + return varVersions; +} } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java index d56c778..d201157 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java @@ -321,4 +321,7 @@ public void setVarFinal(VarVersionPair pair, int finalType) { public Map getMapOriginalVarIndices() { return mapOriginalVarIndices; } + public VarTypeProcessor getTypeProcessor() { + return typeProcessor; +} } diff --git a/testData/classes/pkg/TestVarType.class b/testData/classes/pkg/TestVarType.class new file mode 100644 index 0000000000000000000000000000000000000000..de04ad7bc6fc7b7c1c7489d444a9e6e97f89ab09 GIT binary patch literal 375 zcmZ9HKTpFz48@=Gw}h64AhELn1Cdy%Sdfqq0S2P(mvAaqLQB>8olmq6M-YzHlZp#fj^dkatn)_T z4NPg8Q-QsIFcdgv)twGtW1|Zf9yy6A_4VvA({-w{LJOj#%2h$EY4!TkY3|Kbu$}yC zdm`|&d819qUSfDJ1i@`Jt8;yBT3q;N$fI^X?19s86~N+)Q_%WUF}={chcD! Date: Wed, 16 Sep 2015 00:49:12 -0400 Subject: [PATCH 30/73] More merging. It does some things better. But still not anywhere near good enough :( --- .../decompiler/stats/BasicBlockStatement.java | 10 ++++ .../decompiler/vars/VarDefinitionHelper.java | 54 +++++++++++++++---- 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java index 762fd01..ea7e611 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java @@ -24,6 +24,7 @@ import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.vars.StartEndPair; public class BasicBlockStatement extends Statement { @@ -94,4 +95,13 @@ public Statement getSimpleCopy() { public BasicBlock getBlock() { return block; } + + @Override + public StartEndPair getStartEndRange() { + if (block.size() > 0) { + return new StartEndPair(block.getStartInstruction(),block.getEndInstruction()); + } else { + return null; + } + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java index 7abcec8..8c42c0c 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java @@ -19,6 +19,7 @@ import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector; import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement; @@ -47,11 +48,14 @@ public class VarDefinitionHelper { private final VarProcessor varproc; + private final Map> constExprents; + public VarDefinitionHelper(Statement root, StructMethod mt, VarProcessor varproc) { mapVarDefStatements = new HashMap(); mapStatementVars = new HashMap>(); implDefVars = new HashSet(); + constExprents = new HashMap>(); this.varproc = varproc; VarNamesCollector vc = DecompilerContext.getVarNamesCollector(); @@ -472,16 +476,33 @@ private void checkAssignment(AssignmentExprent exp, Map right.getIndex()) { - remaps.put(new VarVersionPair(left), new VarVersionPair(right)); + // constant + VarVersionPair source = new VarVersionPair(left); + if (exp.getRight().type == Exprent.EXPRENT_CONST) { + VarVersionPair target = new VarVersionPair(index,0); + if (index < left.getIndex()) { + remaps.put(source, target); + } + if (!constExprents.containsKey(source)) { + constExprents.put(source, new ArrayList()); + } + List ceList = constExprents.get(source); + ceList.add((ConstExprent) exp.getRight()); + } else { + for (Exprent e : exp.getRight().getAllExprents(true)) { + if (e.type == Exprent.EXPRENT_VAR) { + VarExprent right = (VarExprent)e; + if (varproc.getRemapped(right.getIndex()) == index) { + if (!left.equals(right) && left.getIndex() > right.getIndex()) { + remaps.put(source, new VarVersionPair(right)); + } + return; + } } - return; } - } + if (index < left.getIndex()) { + remaps.put(source, new VarVersionPair(index,0)); + } } } @@ -514,6 +535,7 @@ private void remapVar(Map remap, Exprent exprent List lst = exprent.getAllExprents(true); lst.add(exprent); Map mapExprentMinTypes = varproc.getVarVersions().getTypeProcessor().getMapExprentMinTypes(); + Map mapExprentMaxTypes = varproc.getVarVersions().getTypeProcessor().getMapExprentMaxTypes(); for (Exprent expr : lst) { if (expr.type == Exprent.EXPRENT_VAR) { @@ -523,10 +545,24 @@ private void remapVar(Map remap, Exprent exprent if (new_ != null) { VarType firstMinType = mapExprentMinTypes.get(old); VarType secondMinType = mapExprentMinTypes.get(new_); + VarType type = firstMinType == null ? secondMinType : (secondMinType == null ? firstMinType :VarType.getCommonSupertype(firstMinType, secondMinType)); + if (type == null || firstMinType == null || secondMinType == null) { + // no common supertype, skip the remapping + continue; + } + if (type.typeFamily == CodeConstants.TYPE_FAMILY_OBJECT && mapExprentMaxTypes.get(old) != null && mapExprentMaxTypes.get(new_) != null) { + type = VarType.getCommonMinType(mapExprentMaxTypes.get(old), mapExprentMaxTypes.get(new_)); + } else if (type.typeFamily == CodeConstants.TYPE_FAMILY_OBJECT) { + continue; + } var.setIndex(new_.var); var.setVersion(new_.version); - VarType type = VarType.getCommonSupertype(firstMinType, secondMinType); mapExprentMinTypes.put(new_, type); + if (constExprents.containsKey(old)) { + for (ConstExprent ce : constExprents.get(old)) { + ce.setConstType(type); + } + } } } } From 8ac9ea89c0f38c0fd16568424e3ae274f709ca5f Mon Sep 17 00:00:00 2001 From: cpw Date: Thu, 17 Sep 2015 17:13:17 -0400 Subject: [PATCH 31/73] More tweaks to merging. It's pretty decent. But still no where near good enough. --- .../decompiler/vars/VarDefinitionHelper.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java index 8c42c0c..ca27e47 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java @@ -550,10 +550,18 @@ private void remapVar(Map remap, Exprent exprent // no common supertype, skip the remapping continue; } - if (type.typeFamily == CodeConstants.TYPE_FAMILY_OBJECT && mapExprentMaxTypes.get(old) != null && mapExprentMaxTypes.get(new_) != null) { - type = VarType.getCommonMinType(mapExprentMaxTypes.get(old), mapExprentMaxTypes.get(new_)); - } else if (type.typeFamily == CodeConstants.TYPE_FAMILY_OBJECT) { - continue; + if (type.typeFamily == CodeConstants.TYPE_FAMILY_OBJECT) { + if (mapExprentMaxTypes.get(old) != null && mapExprentMaxTypes.get(new_) != null) { + type = VarType.getCommonMinType(mapExprentMaxTypes.get(old), mapExprentMaxTypes.get(new_)); + } else if (firstMinType.arrayDim != secondMinType.arrayDim) { + continue; + } else { + type = VarType.getCommonMinType(firstMinType, secondMinType); + // couldn't find a sane common supertype, we're not gonna be able to merge + if (type == null || type == VarType.VARTYPE_NULL) { + continue; + } + } } var.setIndex(new_.var); var.setVersion(new_.version); @@ -563,6 +571,11 @@ private void remapVar(Map remap, Exprent exprent ce.setConstType(type); } } + if (constExprents.containsKey(new_)) { + for (ConstExprent ce : constExprents.get(new_)) { + ce.setConstType(type); + } + } } } } From 42cf0ce83e1c02a459430065811093cd923073b4 Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Fri, 18 Sep 2015 14:36:48 -0700 Subject: [PATCH 32/73] New aproach to merging variables. Use top down manual scoping. 341 --- .../decompiler/vars/VarDefinitionHelper.java | 378 ++++++++++++------ 1 file changed, 258 insertions(+), 120 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java index ca27e47..0c03cd5 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java @@ -25,8 +25,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; -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.IfStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructMethod; @@ -35,7 +34,6 @@ import java.util.*; import java.util.Map.Entry; -import java.util.AbstractMap.SimpleImmutableEntry; public class VarDefinitionHelper { @@ -48,15 +46,16 @@ public class VarDefinitionHelper { private final VarProcessor varproc; - private final Map> constExprents; + private final Statement root; + private final StructMethod mt; public VarDefinitionHelper(Statement root, StructMethod mt, VarProcessor varproc) { - mapVarDefStatements = new HashMap(); mapStatementVars = new HashMap>(); implDefVars = new HashSet(); - constExprents = new HashMap>(); this.varproc = varproc; + this.root = root; + this.mt = mt; VarNamesCollector vc = DecompilerContext.getVarNamesCollector(); @@ -98,8 +97,6 @@ public VarDefinitionHelper(Statement root, StructMethod mt, VarProcessor varproc vc.addName("this"); } - mergeVars(root); - // catch variables are implicitly defined LinkedList stack = new LinkedList(); stack.add(root); @@ -133,15 +130,9 @@ else if (st.type == Statement.TYPE_TRYCATCH) { public void setVarDefinitions() { VarNamesCollector vc = DecompilerContext.getVarNamesCollector(); - Map> trackingMap = new HashMap>(); for (Entry en : mapVarDefStatements.entrySet()) { Statement stat = en.getValue(); - if (!trackingMap.containsKey(stat)) { - trackingMap.put(stat, new HashMap()); - } - Map scopedMap = trackingMap.get(stat); Integer index = en.getKey(); - int newindex = varproc.getRemapped(index); setupLVTs(stat); if (implDefVars.contains(index)) { @@ -156,7 +147,7 @@ public void setVarDefinitions() { DoStatement dstat = (DoStatement)stat; if (dstat.getLooptype() == DoStatement.LOOP_FOR) { - if (dstat.getInitExprent() != null && setDefinition(dstat.getInitExprent(), index, scopedMap)) { + if (dstat.getInitExprent() != null && setDefinition(dstat.getInitExprent(), index)) { continue; } else { @@ -198,12 +189,7 @@ else if (first.getExprents() == null) { // search for the first assignement to var [index] int addindex = 0; for (Exprent expr : lst) { - - if (scopedMap.containsKey(newindex)) { - defset = true; - break; - } - if (setDefinition(expr, index, scopedMap)) { + if (setDefinition(expr, index)) { defset = true; break; } @@ -229,13 +215,17 @@ else if (first.getExprents() == null) { if (varproc.getLVT() != null) { Map vars = varproc.getLVT().getVars(stat); if (vars.containsKey(var.getIndex())) { - var.setLVT(vars.get(var.getIndex())); + var.setLVT(vars.get(var.getIndex())); } } lst.add(addindex, var); } } +if (mt.getName().equals("func_180655_c")){ + System.out.println("asdf"); +} + mergeVars(root); } @@ -374,14 +364,13 @@ private static List getAllVars(List lst) { return res; } - private static boolean setDefinition(Exprent expr, Integer index, Map stats) { + private static boolean setDefinition(Exprent expr, Integer index) { if (expr.type == Exprent.EXPRENT_ASSIGNMENT) { Exprent left = ((AssignmentExprent)expr).getLeft(); if (left.type == Exprent.EXPRENT_VAR) { VarExprent var = (VarExprent)left; if (var.getIndex() == index.intValue()) { var.setDefinition(true); - stats.put(index, var); return true; } } @@ -434,150 +423,299 @@ private void setupLVTs(Exprent exprent, Map lvts) { } } - private void mergeVars(Statement stat) { - Map remaps = new HashMap(); - mergeVars(stat, remaps); - if (!remaps.isEmpty()) { - for (Entry entry : remaps.entrySet()) { - VarVersionPair end = entry.getKey(); - while (remaps.containsKey(end)) { - end = remaps.get(end); - } - remaps.put(entry.getKey(), end); + private VPPEntry mergeVars(Statement stat) { + Map parent = new HashMap(); // Always empty dua! + MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); + + int index = 0; + if (!mt.hasModifier(CodeConstants.ACC_STATIC)) { + parent.put(index, new VarVersionPair(index++, 0)); + } + + for (VarType var : md.params) { + parent.put(index, new VarVersionPair(index, 0)); + index += var.stackSize; + } + + Map blacklist = new HashMap(); + VPPEntry remap = mergeVars(stat, parent, new HashMap(), blacklist); + VPPEntry ast = remap; + while (remap != null) { + //System.out.println("Remapping: " + remap.getKey() + " -> " + remap.getValue()); + if (!remapVar(stat, remap.getKey(), remap.getValue())) { + blacklist.put(remap.getKey(), remap.getValue()); + } + remap = mergeVars(stat, parent, new HashMap(), blacklist); + if (ast.equals(remap)){ + System.currentTimeMillis(); } - remapVar(remaps, stat); } + return null; } - private void mergeVars(Statement stat, Map remaps) { + + private VPPEntry mergeVars(Statement stat, Map parent, Map leaked, Map blacklist) { + Map this_vars = new HashMap(); + if (parent.size() > 0) + this_vars.putAll(parent); + + if (stat.getVarDefinitions().size() > 0) { + for (int x = 0; x < stat.getVarDefinitions().size(); x++) { + Exprent exp = stat.getVarDefinitions().get(x); + if (exp.type == Exprent.EXPRENT_VAR) { + VarExprent var = (VarExprent)exp; + int index = varproc.getRemapped(var.getIndex()); + if (this_vars.containsKey(index)) { + stat.getVarDefinitions().remove(x); + return new VPPEntry(var, this_vars.get(index)); + } + this_vars.put(index, new VarVersionPair(var)); + leaked.put(index, new VarVersionPair(var)); + } + } + } + + Map scoped = null; + switch (stat.type) { // These are the type of statements that leak vars + case Statement.TYPE_BASICBLOCK: + case Statement.TYPE_GENERAL: + case Statement.TYPE_ROOT: + case Statement.TYPE_SEQUENCE: + scoped = leaked; + } + if (stat.getExprents() == null) { for (Object obj : stat.getSequentialObjects()) { if (obj instanceof Statement) { - mergeVars((Statement)obj, remaps); + Statement st = (Statement)obj; + + //Map blacklist_n = new HashMap(); + Map leaked_n = new HashMap(); + VPPEntry remap = mergeVars(st, this_vars, leaked_n, blacklist); + + if (remap != null) { + return remap; + } + /* TODO: See if we can optimize and only go up till needed. + while (remap != null) { + System.out.println("Remapping: " + remap.getKey() + " -> " + remap.getValue()); + VarVersionPair var = parent.get(varproc.getRemapped(remap.getValue().var)); + if (remap.getValue().equals(var)) { //Drill up to original declaration. + return remap; + } + if (!remapVar(stat, remap.getKey(), remap.getValue())) { + blacklist_n.put(remap.getKey(), remap.getValue()); + } + leaked_n.clear(); + remap = mergeVars(st, this_vars, leaked_n, blacklist_n); + } + */ + + if (leaked_n.size() > 0) { + if (stat.type == Statement.TYPE_IF) { + IfStatement ifst = (IfStatement)stat; + if (obj == ifst.getIfstat() || obj == ifst.getElsestat()) { + leaked_n.clear(); // Force no leaking at the end of if blocks + // We may need to do this for Switches as well.. But havent run into that issue yet... + } + } + this_vars.putAll(leaked_n); + } } - else if (obj instanceof AssignmentExprent) { - checkAssignment((AssignmentExprent)obj, remaps); + else if (obj instanceof Exprent) { + VPPEntry ret = processExprent((Exprent)obj, this_vars, scoped, blacklist); + if (ret != null) { + return ret; + } } } } else { for (Exprent exp : stat.getExprents()) { - if (exp instanceof AssignmentExprent) { - checkAssignment((AssignmentExprent)exp, remaps); + VPPEntry ret = processExprent(exp, this_vars, scoped, blacklist); + if (ret != null) { + return ret; } } } + return null; // We made it with no remaps!!!!!!! } - private void checkAssignment(AssignmentExprent exp, Map remaps) { - if (exp.getLeft().type != Exprent.EXPRENT_VAR) { - return; + private VPPEntry processExprent(Exprent exp, Map this_vars, Map leaked, Map blacklist) { + VarExprent var = null; + + if (exp.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent ass = (AssignmentExprent)exp; + if (ass.getLeft().type != Exprent.EXPRENT_VAR) { + return null; + } + + var = (VarExprent)ass.getLeft(); + } + else if (exp.type == Exprent.EXPRENT_VAR) { + var = (VarExprent)exp; } - VarExprent left = (VarExprent)exp.getLeft(); - int index = varproc.getRemapped(left.getIndex()); + if (var == null) { + return null; + } - // constant - VarVersionPair source = new VarVersionPair(left); - if (exp.getRight().type == Exprent.EXPRENT_CONST) { - VarVersionPair target = new VarVersionPair(index,0); - if (index < left.getIndex()) { - remaps.put(source, target); - } - if (!constExprents.containsKey(source)) { - constExprents.put(source, new ArrayList()); - } - List ceList = constExprents.get(source); - ceList.add((ConstExprent) exp.getRight()); - } else { - for (Exprent e : exp.getRight().getAllExprents(true)) { - if (e.type == Exprent.EXPRENT_VAR) { - VarExprent right = (VarExprent)e; - if (varproc.getRemapped(right.getIndex()) == index) { - if (!left.equals(right) && left.getIndex() > right.getIndex()) { - remaps.put(source, new VarVersionPair(right)); - } - return; - } - } - } - if (index < left.getIndex()) { - remaps.put(source, new VarVersionPair(index,0)); - } + if (!var.isDefinition()) { + return null; } - } - private void remapVar(Map remap, Statement stat) { - if (remap == null || stat == null) { - return; + int index = varproc.getRemapped(var.getIndex()); + VarVersionPair new_ = this_vars.get(index); + if (new_ != null) { + VarVersionPair old = new VarVersionPair(var); + VarVersionPair black = blacklist.get(old); + if (black == null || !black.equals(new_)) { + return new VPPEntry(var, this_vars.get(index)); + } } + this_vars.put(index, new VarVersionPair(var)); + if (leaked != null) { + leaked.put(index, new VarVersionPair(var)); + } + + return null; + } + + private boolean remapVar(Statement stat, VarVersionPair from, VarVersionPair to) { + if (from.equals(to)) + throw new IllegalArgumentException("Shit went wrong: " + from); + boolean success = false; if (stat.getExprents() == null) { for (Object obj : stat.getSequentialObjects()) { if (obj instanceof Statement) { - remapVar(remap, (Statement)obj); + success |= remapVar((Statement)obj, from, to); } else if (obj instanceof Exprent) { - remapVar(remap, (Exprent)obj); + if (remapVar((Exprent)obj, from, to)) { + success = true; + } } } } else { - for (Exprent exp : stat.getExprents()) { - remapVar(remap, exp); + boolean remapped = false; + for (int x = 0; x < stat.getExprents().size(); x++) { + Exprent exp = stat.getExprents().get(x); + if (remapVar(exp, from, to)) { + remapped = true; + if (exp.type == Exprent.EXPRENT_VAR) { + if (!((VarExprent)exp).isDefinition()) { + stat.getExprents().remove(x); + x--; + } + } + } } + success |= remapped; } + return success; } - private void remapVar(Map remap, Exprent exprent) { - if (exprent == null) { - return; + private boolean remapVar(Exprent exprent, VarVersionPair from, VarVersionPair to) { + if (exprent == null) { // Sometimes there are null exprents? + return false; } List lst = exprent.getAllExprents(true); lst.add(exprent); Map mapExprentMinTypes = varproc.getVarVersions().getTypeProcessor().getMapExprentMinTypes(); Map mapExprentMaxTypes = varproc.getVarVersions().getTypeProcessor().getMapExprentMaxTypes(); + boolean remapped = false; + for (Exprent expr : lst) { - if (expr.type == Exprent.EXPRENT_VAR) { - VarExprent var = (VarExprent)expr; - VarVersionPair old = new VarVersionPair(var); - VarVersionPair new_ = remap.get(old); - if (new_ != null) { - VarType firstMinType = mapExprentMinTypes.get(old); - VarType secondMinType = mapExprentMinTypes.get(new_); - VarType type = firstMinType == null ? secondMinType : (secondMinType == null ? firstMinType :VarType.getCommonSupertype(firstMinType, secondMinType)); - if (type == null || firstMinType == null || secondMinType == null) { - // no common supertype, skip the remapping - continue; - } - if (type.typeFamily == CodeConstants.TYPE_FAMILY_OBJECT) { - if (mapExprentMaxTypes.get(old) != null && mapExprentMaxTypes.get(new_) != null) { - type = VarType.getCommonMinType(mapExprentMaxTypes.get(old), mapExprentMaxTypes.get(new_)); - } else if (firstMinType.arrayDim != secondMinType.arrayDim) { - continue; - } else { - type = VarType.getCommonMinType(firstMinType, secondMinType); - // couldn't find a sane common supertype, we're not gonna be able to merge - if (type == null || type == VarType.VARTYPE_NULL) { - continue; - } - } - } - var.setIndex(new_.var); - var.setVersion(new_.version); - mapExprentMinTypes.put(new_, type); - if (constExprents.containsKey(old)) { - for (ConstExprent ce : constExprents.get(old)) { - ce.setConstType(type); - } + if (expr.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent ass = (AssignmentExprent)expr; + if (ass.getLeft().type == Exprent.EXPRENT_VAR && ass.getRight().type == Exprent.EXPRENT_CONST) { + VarVersionPair left = new VarVersionPair((VarExprent)ass.getLeft()); + if (!left.equals(from) && !left.equals(to)) { + continue; } - if (constExprents.containsKey(new_)) { - for (ConstExprent ce : constExprents.get(new_)) { - ce.setConstType(type); - } + + ConstExprent right = (ConstExprent)ass.getRight(); + VarType merged = getMergedType(mapExprentMinTypes.get(from), mapExprentMinTypes.get(to), + mapExprentMaxTypes.get(from), mapExprentMaxTypes.get(to)); + + if (merged == null) { // Types incompatible, do not merge + continue; } + + right.setConstType(merged); } } + else if (expr.type == Exprent.EXPRENT_VAR) { + VarExprent var = (VarExprent)expr; + VarVersionPair old = new VarVersionPair(var); + if (!old.equals(from)) { + continue; + } + VarType merged = getMergedType(mapExprentMinTypes.get(from), mapExprentMinTypes.get(to), + mapExprentMaxTypes.get(from), mapExprentMaxTypes.get(to)); + if (merged == null) { // Types incompatible, do not merge + continue; + } + + var.setIndex(to.var); + var.setVersion(to.version); + var.setVarType(merged); + if (var.isDefinition()) { + var.setDefinition(false); + } + mapExprentMinTypes.put(to, merged); + remapped = true; + } + } + return remapped; + } + + private VarType getMergedType(VarType firstMin, VarType secondMin, VarType firstMax, VarType secondMax) { + if (firstMin != null && firstMin.equals(secondMin)) { + return firstMin; // Short circuit this for simplicities sake + } + VarType type = firstMin == null ? secondMin : (secondMin == null ? firstMin : VarType.getCommonSupertype(firstMin, secondMin)); + if (type == null || firstMin == null || secondMin == null) { + return null; // no common supertype, skip the remapping + } + if (type.typeFamily == CodeConstants.TYPE_FAMILY_OBJECT) { + if (firstMax != null && secondMax != null) { + type = VarType.getCommonMinType(firstMax, secondMax); + } else if (firstMin.arrayDim != secondMin.arrayDim) { + return null; // Don't merge is arrays don't match. + } else { + type = VarType.getCommonMinType(firstMin, secondMin); + // couldn't find a sane common supertype, we're not gonna be able to merge + if (type == null || type == VarType.VARTYPE_NULL) { + return null; + } + } + } + return type; + } + + //Helper classes because Java is dumb and doesn't have a Pair class + private static class SimpleEntry implements Entry { + private K key; + private V value; + public SimpleEntry(K key, V value) { + this.key = key; + this.value = value; + } + @Override public K getKey() { return key; } + @Override public V getValue() { return value; } + @Override + public V setValue(V value) { + V tmp = this.value; + this.value = value; + return tmp; + } + } + private static class VPPEntry extends SimpleEntry { + public VPPEntry(VarExprent key, VarVersionPair value) { + super(new VarVersionPair(key), value); } } } \ No newline at end of file From a0351b03097955eeac9fc6cba0e22242365280b5 Mon Sep 17 00:00:00 2001 From: cpw Date: Thu, 24 Sep 2015 00:00:19 -0400 Subject: [PATCH 33/73] If it's a null constant, don't give it a type. Cleans up some more errors. --- .../modules/decompiler/vars/VarDefinitionHelper.java | 3 +++ test/org/jetbrains/java/decompiler/LVTTest.java | 12 +++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java index 0c03cd5..9ecb28b 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java @@ -637,6 +637,9 @@ private boolean remapVar(Exprent exprent, VarVersionPair from, VarVersionPair to } ConstExprent right = (ConstExprent)ass.getRight(); + if (right.getConstType() == VarType.VARTYPE_NULL) { + continue; + } VarType merged = getMergedType(mapExprentMinTypes.get(from), mapExprentMinTypes.get(to), mapExprentMaxTypes.get(from), mapExprentMaxTypes.get(to)); diff --git a/test/org/jetbrains/java/decompiler/LVTTest.java b/test/org/jetbrains/java/decompiler/LVTTest.java index d2a8f1c..52428c7 100644 --- a/test/org/jetbrains/java/decompiler/LVTTest.java +++ b/test/org/jetbrains/java/decompiler/LVTTest.java @@ -35,10 +35,20 @@ public void setUp() throws IOException { super.setUp(); fixture.cleanup = false; } - @Test public void testMatch1() { doTest("pkg/TestPPMM"); } +// @Test public void testMatch1() { doTest("pkg/TestPPMM"); } // @Test public void testMatchLM() { doTest("pkg/TestLexManosLVT"); } // @Test public void testMatch1() { doTest("pkg/TestLVT"); } // @Test public void testMatch2() { doTest("pkg/TestLVTScoping"); } // @Test public void testMCWorld() { doTest("net/minecraft/world/World"); } // @Test public void testMCGuiCmdBlock() { doTest("net/minecraft/client/gui/GuiCommandBlock"); } + +// @Test public void testMCWorld() { doTest("net/minecraft/world/World"); } + +// @Test public void testMCGuiCmdBlock() { doTest("net/minecraft/client/gui/GuiCommandBlock"); } + +// @Test public void testMCBlockFence() { doTest("net/minecraft/block/BlockFence"); } + +// @Test public void testMCAbstractTexture() { doTest("net/minecraft/client/multiplayer/ServerAddress"); } +// @Test public void testMCAbstractTexture() { doTest("net/minecraft/client/resources/AbstractResourcePack"); } + @Test public void testMCGuiShareToLan() { doTest("net/minecraft/client/gui/GuiShareToLan"); } } From b065f7aef331543501429be47e0f36a22933c617 Mon Sep 17 00:00:00 2001 From: cpw Date: Thu, 24 Sep 2015 12:48:31 -0400 Subject: [PATCH 34/73] Bind the LVTs at the time we create the exprents. The offset is absolute at that point, and won't be incorrect. --- .../decompiler/modules/decompiler/ExprProcessor.java | 10 +++++++--- .../modules/decompiler/vars/LocalVariableTable.java | 8 ++------ .../modules/decompiler/vars/VarDefinitionHelper.java | 3 +-- .../modules/decompiler/vars/VarProcessor.java | 12 +++++++++--- test/org/jetbrains/java/decompiler/LVTTest.java | 6 +++--- 5 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java index 0425fbc..7b94e5b 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java @@ -382,7 +382,9 @@ else if (cn instanceof LinkConstant) { case opc_fload: case opc_dload: case opc_aload: - pushEx(stack, exprlist, new VarExprent(instr.getOperand(0), vartypes[instr.opcode - opc_iload], varProcessor)); + VarExprent varExprent = new VarExprent(instr.getOperand(0), vartypes[instr.opcode - opc_iload], varProcessor); + varProcessor.findLVT(varExprent, bytecode_offset+instr.length()); + pushEx(stack, exprlist, varExprent); break; case opc_iaload: case opc_laload: @@ -412,8 +414,9 @@ else if (cn instanceof LinkConstant) { case opc_astore: Exprent top = stack.pop(); int varindex = instr.getOperand(0); - AssignmentExprent assign = - new AssignmentExprent(new VarExprent(varindex, vartypes[instr.opcode - opc_istore], varProcessor), top, bytecode_offsets); + varExprent = new VarExprent(varindex, vartypes[instr.opcode - opc_istore], varProcessor); + varProcessor.findLVT(varExprent, bytecode_offset+instr.length()); + AssignmentExprent assign = new AssignmentExprent(varExprent, top, bytecode_offsets); exprlist.add(assign); break; case opc_iastore: @@ -475,6 +478,7 @@ else if (cn instanceof LinkConstant) { break; case opc_iinc: VarExprent vevar = new VarExprent(instr.getOperand(0), VarType.VARTYPE_INT, varProcessor); + varProcessor.findLVT(vevar,bytecode_offset+instr.length()); exprlist.add(new AssignmentExprent(vevar, new FunctionExprent( instr.getOperand(1) < 0 ? FunctionExprent.FUNCTION_SUB : FunctionExprent.FUNCTION_ADD, Arrays .asList(vevar.copy(), new ConstExprent(VarType.VARTYPE_INT, Math.abs(instr.getOperand(1)), null)), diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java index fe45b11..4193f56 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java @@ -41,11 +41,7 @@ public void mergeLVTs(LocalVariableTable otherLVT) { mapLVT = null; // Invalidate the cache and rebuild it. } - public LVTVariable find(int index, Statement stat) { - BitSet values = new BitSet(); - MethodProcessorRunnable.getOffset(stat, values); - int start = values.nextSetBit(0); - int end = values.length()-1; + public LVTVariable find(int index, Integer bytecodeOffset) { //System.out.println(indent + stat.getClass().getSimpleName() + " (" + start +", " + end + ")"); Map> map = getMapVarNames(); @@ -53,7 +49,7 @@ public LVTVariable find(int index, Statement stat) { return null; } for (LVTVariable lvt : map.get(index)) { - if (lvt.start >= start && lvt.end <= end) { + if (lvt.start == bytecodeOffset) { return lvt; } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java index 9ecb28b..6d8ab12 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java @@ -133,7 +133,7 @@ public void setVarDefinitions() { for (Entry en : mapVarDefStatements.entrySet()) { Statement stat = en.getValue(); Integer index = en.getKey(); - setupLVTs(stat); +// setupLVTs(stat); if (implDefVars.contains(index)) { // already implicitly defined @@ -452,7 +452,6 @@ private VPPEntry mergeVars(Statement stat) { } return null; } - private VPPEntry mergeVars(Statement stat, Map parent, Map leaked, Map blacklist) { Map this_vars = new HashMap(); if (parent.size() > 0) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java index 691797e..bbf17ad 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java @@ -17,6 +17,7 @@ import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector; +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; import org.jetbrains.java.decompiler.struct.StructMethod; @@ -125,7 +126,9 @@ public VarType getVarType(VarVersionPair pair) { } public void setVarType(VarVersionPair pair, VarType type) { - varVersions.setVarType(pair, type); + if (varVersions != null) { + varVersions.setVarType(pair, type); + } } public String getVarName(VarVersionPair pair) { @@ -160,8 +163,11 @@ public LocalVariableTable getLVT() { return this.lvt; } - public LVTVariable findLVT(int index, Statement stat) { - return this.lvt == null ? null : lvt.find(index, stat); + public void findLVT(VarExprent varExprent, int bytecodeOffset) { + LVTVariable var = this.lvt == null ? null : lvt.find(varExprent.getIndex(), bytecodeOffset); + if (var != null) { + varExprent.setLVT(var); + } } public int getRemapped(int index) { diff --git a/test/org/jetbrains/java/decompiler/LVTTest.java b/test/org/jetbrains/java/decompiler/LVTTest.java index 52428c7..d4771bb 100644 --- a/test/org/jetbrains/java/decompiler/LVTTest.java +++ b/test/org/jetbrains/java/decompiler/LVTTest.java @@ -42,13 +42,13 @@ public void setUp() throws IOException { // @Test public void testMCWorld() { doTest("net/minecraft/world/World"); } // @Test public void testMCGuiCmdBlock() { doTest("net/minecraft/client/gui/GuiCommandBlock"); } -// @Test public void testMCWorld() { doTest("net/minecraft/world/World"); } + @Test public void testMCWorld() { doTest("net/minecraft/world/World"); } // @Test public void testMCGuiCmdBlock() { doTest("net/minecraft/client/gui/GuiCommandBlock"); } // @Test public void testMCBlockFence() { doTest("net/minecraft/block/BlockFence"); } // @Test public void testMCAbstractTexture() { doTest("net/minecraft/client/multiplayer/ServerAddress"); } -// @Test public void testMCAbstractTexture() { doTest("net/minecraft/client/resources/AbstractResourcePack"); } - @Test public void testMCGuiShareToLan() { doTest("net/minecraft/client/gui/GuiShareToLan"); } +// @Test public void testMCAbstractResourcePack() { doTest("net/minecraft/client/resources/AbstractResourcePack"); } +// @Test public void testMCGuiShareToLan() { doTest("net/minecraft/client/gui/GuiShareToLan"); } } From 97779c0a2088cb5153b21f1b3f4f7459cc9c4ec0 Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Sun, 27 Sep 2015 21:17:03 -0700 Subject: [PATCH 35/73] Some code cleanup damn cpw --- .../java/decompiler/code/cfg/BasicBlock.java | 18 +++++------ .../decompiler/stats/BasicBlockStatement.java | 8 ++--- .../modules/decompiler/stats/DoStatement.java | 5 --- .../modules/decompiler/stats/IfStatement.java | 8 +++-- .../decompiler/stats/RootStatement.java | 6 ++-- .../decompiler/stats/SequenceStatement.java | 5 --- .../modules/decompiler/stats/Statement.java | 18 +++++------ .../decompiler/stats/SwitchStatement.java | 16 +++++----- .../decompiler/vars/LocalVariableTable.java | 32 ++++++------------- 9 files changed, 48 insertions(+), 68 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/code/cfg/BasicBlock.java b/src/org/jetbrains/java/decompiler/code/cfg/BasicBlock.java index a36047b..407aaa9 100644 --- a/src/org/jetbrains/java/decompiler/code/cfg/BasicBlock.java +++ b/src/org/jetbrains/java/decompiler/code/cfg/BasicBlock.java @@ -264,17 +264,17 @@ public void setPredExceptions(List predExceptions) { } public int getStartInstruction() { - if (seq.isEmpty()) { - return 0; - } - return instrOldOffsets.get(0); + if (seq.isEmpty()) { + return 0; + } + return instrOldOffsets.get(0); } public int getEndInstruction() { - if (seq.isEmpty()) { - return 0; - } - int end = seq.getLastInstr().length(); - return end + instrOldOffsets.get(size() -1); + if (seq.isEmpty()) { + return 0; + } + int end = seq.getLastInstr().length(); + return end + instrOldOffsets.get(size() -1); } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java index ea7e611..e68cb97 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java @@ -97,11 +97,11 @@ public BasicBlock getBlock() { } @Override - public StartEndPair getStartEndRange() { - if (block.size() > 0) { - return new StartEndPair(block.getStartInstruction(),block.getEndInstruction()); + public StartEndPair getStartEndRange() { + if (block.size() > 0) { + return new StartEndPair(block.getStartInstruction(), block.getEndInstruction()); } else { - return null; + return null; } } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java index 0768912..36cd7fb 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java @@ -242,9 +242,4 @@ public int getLooptype() { public void setLooptype(int looptype) { this.looptype = looptype; } - - @Override - public StartEndPair getStartEndRange() { - return super.getStartEndRange(); - } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java index f216b73..8f3e467 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java @@ -465,7 +465,9 @@ public boolean match(MatchNode matchNode, MatchEngine engine) { } @Override - public StartEndPair getStartEndRange() { - return StartEndPair.join(super.getStartEndRange(),ifstat != null ? ifstat.getStartEndRange() : null,elsestat != null ? elsestat.getStartEndRange(): null); - } + public StartEndPair getStartEndRange() { + return StartEndPair.join(super.getStartEndRange(), + ifstat != null ? ifstat.getStartEndRange() : null, + elsestat != null ? elsestat.getStartEndRange(): null); + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/RootStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/RootStatement.java index 0a1e798..d8329ff 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/RootStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/RootStatement.java @@ -49,7 +49,7 @@ public void setDummyExit(DummyExitStatement dummyExit) { } @Override - public StartEndPair getStartEndRange() { - return first.getStartEndRange(); - } + public StartEndPair getStartEndRange() { + return first.getStartEndRange(); + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java index b3785fb..81d31a5 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java @@ -138,9 +138,4 @@ public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { public Statement getSimpleCopy() { return new SequenceStatement(); } - - @Override - public StartEndPair getStartEndRange() { - return super.getStartEndRange(); - } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java index 806c4d5..f71ed84 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java @@ -941,15 +941,15 @@ public boolean match(MatchNode matchNode, MatchEngine engine) { private StartEndPair endpoints; public StartEndPair getStartEndRange() { - if (endpoints == null) { - int start = Integer.MAX_VALUE; - int end = Integer.MIN_VALUE; - for (Statement st : getStats()) { - start = Math.min(start, st.getBasichead().getBlock().getStartInstruction()); - end = Math.max(end, st.getBasichead().getBlock().getEndInstruction()); - } - endpoints = new StartEndPair(start,end); + if (endpoints == null) { + int start = Integer.MAX_VALUE; + int end = Integer.MIN_VALUE; + for (Statement st : getStats()) { + start = Math.min(start, st.getBasichead().getBlock().getStartInstruction()); + end = Math.max(end, st.getBasichead().getBlock().getEndInstruction()); } - return endpoints; + endpoints = new StartEndPair(start, end); + } + return endpoints; } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java index 8d7127c..3e27201 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java @@ -368,13 +368,13 @@ public List> getCaseValues() { } @Override - public StartEndPair getStartEndRange() { - StartEndPair[] sepairs = new StartEndPair[caseStatements.size() + 1]; - int i = 0; - sepairs[i++] = super.getStartEndRange(); - for (Statement st : caseStatements) { - sepairs[i++] = st.getStartEndRange(); - } - return StartEndPair.join(sepairs); + public StartEndPair getStartEndRange() { + StartEndPair[] sepairs = new StartEndPair[caseStatements.size() + 1]; + int i = 0; + sepairs[i++] = super.getStartEndRange(); + for (Statement st : caseStatements) { + sepairs[i++] = st.getStartEndRange(); } + return StartEndPair.join(sepairs); + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java index 4193f56..729a3d2 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LocalVariableTable.java @@ -1,15 +1,11 @@ package org.jetbrains.java.decompiler.modules.decompiler.vars; import java.util.ArrayList; -import java.util.BitSet; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import org.jetbrains.java.decompiler.main.rels.MethodProcessorRunnable; -import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; public class LocalVariableTable { @@ -74,8 +70,8 @@ private void buildNameMap() { versions.put(lvt.index, idx); List lvtList = mapLVT.get(lvt.index); if (lvtList == null) { - lvtList = new ArrayList(); - mapLVT.put(lvt.index, lvtList); + lvtList = new ArrayList(); + mapLVT.put(lvt.index, lvtList); } lvtList.add(lvt); } @@ -86,24 +82,16 @@ public List getCandidates(int index) { } public Map getVars(Statement statement) { - HashMap ret = new HashMap(); - if (statement == null) return ret; - StartEndPair sepair = statement.getStartEndRange(); + Map ret = new HashMap(); + if (statement == null) { + return ret; + } + StartEndPair sepair = statement.getStartEndRange(); if (endpoints.containsKey(sepair)) { - for (LVTVariable lvt : endpoints.get(sepair)) { - ret.put(lvt.index,lvt); - } + for (LVTVariable lvt : endpoints.get(sepair)) { + ret.put(lvt.index, lvt); + } } -// for (Entry> entry : endpoints.get(end)) { -// for (LVTVariable lvt : entry.getValue()) { -// if (lvt.start >= start && lvt.end <= end) { -// if (ret.containsKey(entry.getKey())) { -// System.out.println("DUPLICATE INDEX WHAT THE FUCK: " + entry.getKey()); -// } -// ret.put(entry.getKey(), lvt); -// } -// } -// } return ret; } } \ No newline at end of file From ec3558a5473045e647365b39187c259e1690042d Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Sun, 27 Sep 2015 21:17:47 -0700 Subject: [PATCH 36/73] Propogate LVT data post merge. And fix injected var defs LVT association. --- .../modules/decompiler/exps/VarExprent.java | 6 +- .../decompiler/vars/VarDefinitionHelper.java | 155 +++++++++++++++--- 2 files changed, 140 insertions(+), 21 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java index 3d5f03b..05a4f79 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java @@ -232,10 +232,14 @@ public boolean match(MatchNode matchNode, MatchEngine engine) { public void setLVT(LVTVariable lvt) { this.lvt = lvt; if (processor != null) { - processor.setVarType(new VarVersionPair(this),lvt.getVarType()); + processor.setVarType(new VarVersionPair(this), lvt.getVarType()); } } + public LVTVariable getLVT() { + return this.lvt; + } + @Override public String toString() { return lvt != null ? lvt.name : "var_" + index + "_" + version; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java index 6d8ab12..cc5e5e4 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java @@ -133,7 +133,6 @@ public void setVarDefinitions() { for (Entry en : mapVarDefStatements.entrySet()) { Statement stat = en.getValue(); Integer index = en.getKey(); -// setupLVTs(stat); if (implDefVars.contains(index)) { // already implicitly defined @@ -213,19 +212,18 @@ else if (first.getExprents() == null) { var.setDefinition(true); if (varproc.getLVT() != null) { - Map vars = varproc.getLVT().getVars(stat); - if (vars.containsKey(var.getIndex())) { - var.setLVT(vars.get(var.getIndex())); + LVTVariable lvt = findLVT(index.intValue(), stat); + if (lvt != null) { + var.setLVT(lvt); } } lst.add(addindex, var); } } -if (mt.getName().equals("func_180655_c")){ - System.out.println("asdf"); -} + mergeVars(root); + propogateLVTs(root); } @@ -233,6 +231,54 @@ else if (first.getExprents() == null) { // private methods // ***************************************************************************** + private LVTVariable findLVT(int index, Statement stat) { + if (stat.getExprents() == null) { + for (Object obj : stat.getSequentialObjects()) { + if (obj instanceof Statement) { + LVTVariable lvt = findLVT(index, (Statement)obj); + if (lvt != null) { + return lvt; + } + } + else if (obj instanceof Exprent) { + LVTVariable lvt = findLVT(index, (Exprent)obj); + if (lvt != null) { + return lvt; + } + } + } + } + else { + for (Exprent exp : stat.getExprents()) { + LVTVariable lvt = findLVT(index, exp); + if (lvt != null) { + return lvt; + } + } + } + return null; + } + + private LVTVariable findLVT(int index, Exprent exp) { + VarExprent var = null; + + if (exp.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent ass = (AssignmentExprent)exp; + if (ass.getLeft().type == Exprent.EXPRENT_VAR) { + var = (VarExprent)ass.getLeft(); + } + } + else if (exp.type == Exprent.EXPRENT_VAR) { + var = (VarExprent)exp; + } + + if (var == null) { + return null; + } + + return var.getIndex() == index ? var.getLVT() : null; + } + private Statement findFirstBlock(Statement stat, Integer varindex) { LinkedList stack = new LinkedList(); @@ -378,31 +424,99 @@ private static boolean setDefinition(Exprent expr, Integer index) { return false; } - private void setupLVTs(Statement stat) { - if (stat == null || varproc.getLVT() == null) { + private void propogateLVTs(Statement stat) { + if (varproc.getLVT() == null) { return; } - Map vars = varproc.getLVT().getVars(stat); + MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); + Map types = new HashMap(); + + int index = 0; + if (!mt.hasModifier(CodeConstants.ACC_STATIC)) { + types.put(new VarVersionPair(index, 0), varproc.getLVT().getCandidates(index++).get(0)); + } + + for (VarType var : md.params) { + types.put(new VarVersionPair(index, 0), varproc.getLVT().getCandidates(index).get(0)); + index += var.stackSize; + } + + findTypes(stat, types); + + applyTypes(stat, types); + } + + private void findTypes(Statement stat, Map types) { + if (stat == null) { + return; + } + + for (Exprent exp : stat.getVarDefinitions()) { + findTypes(exp, types); + } + + if (stat.getExprents() == null) { + for (Object obj : stat.getSequentialObjects()) { + if (obj instanceof Statement) { + findTypes((Statement)obj, types); + } + else if (obj instanceof Exprent) { + findTypes((Exprent)obj, types); + } + } + } + else { + for (Exprent exp : stat.getExprents()) { + findTypes(exp, types); + } + } + } + + private void findTypes(Exprent exp, Map types) { + VarExprent var = null; + + if (exp.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent ass = (AssignmentExprent)exp; + if (ass.getLeft().type == Exprent.EXPRENT_VAR) { + var = (VarExprent)ass.getLeft(); + } + } + else if (exp.type == Exprent.EXPRENT_VAR) { + var = (VarExprent)exp; + } + + if (var == null || !var.isDefinition()) { + return; + } + + types.put(new VarVersionPair(var), var.getLVT()); + } + + + private void applyTypes(Statement stat, Map types) { + if (stat == null || types.size() == 0) { + return; + } if (stat.getExprents() == null) { for (Object obj : stat.getSequentialObjects()) { if (obj instanceof Statement) { - setupLVTs((Statement)obj); + applyTypes((Statement)obj, types); } else if (obj instanceof Exprent) { - setupLVTs((Exprent)obj, vars); + applyTypes((Exprent)obj, types); } } } else { for (Exprent exp : stat.getExprents()) { - setupLVTs(exp, vars); + applyTypes(exp, types); } } } - private void setupLVTs(Exprent exprent, Map lvts) { + private void applyTypes(Exprent exprent, Map types) { if (exprent == null) { return; } @@ -412,12 +526,12 @@ private void setupLVTs(Exprent exprent, Map lvts) { for (Exprent expr : lst) { if (expr.type == Exprent.EXPRENT_VAR) { VarExprent var = (VarExprent)expr; - int index = varproc.getRemapped(var.getIndex()); - LVTVariable lvt = lvts.get(index); + LVTVariable lvt = types.get(new VarVersionPair(var)); if (lvt != null) { var.setLVT(lvt); } else { - System.currentTimeMillis(); + System.currentTimeMillis(); + System.out.println("null " + new VarVersionPair(var)); } } } @@ -439,19 +553,20 @@ private VPPEntry mergeVars(Statement stat) { Map blacklist = new HashMap(); VPPEntry remap = mergeVars(stat, parent, new HashMap(), blacklist); - VPPEntry ast = remap; + VPPEntry last = remap; while (remap != null) { //System.out.println("Remapping: " + remap.getKey() + " -> " + remap.getValue()); if (!remapVar(stat, remap.getKey(), remap.getValue())) { blacklist.put(remap.getKey(), remap.getValue()); } remap = mergeVars(stat, parent, new HashMap(), blacklist); - if (ast.equals(remap)){ - System.currentTimeMillis(); + if (last.equals(remap)){ + System.currentTimeMillis(); } } return null; } + private VPPEntry mergeVars(Statement stat, Map parent, Map leaked, Map blacklist) { Map this_vars = new HashMap(); if (parent.size() > 0) From f6acdf7dd26df4eebb27b2561f62f44e643c5f89 Mon Sep 17 00:00:00 2001 From: cpw Date: Mon, 28 Sep 2015 01:26:23 -0400 Subject: [PATCH 37/73] Fix types to use LVT first, and fallback to other type bounds --- .../modules/decompiler/exps/VarExprent.java | 15 ++++++++++++++- .../modules/decompiler/vars/VarTypeProcessor.java | 7 ++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java index 05a4f79..933b046 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java @@ -22,6 +22,7 @@ import org.jetbrains.java.decompiler.main.TextBuffer; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; import org.jetbrains.java.decompiler.modules.decompiler.vars.LVTVariable; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor; @@ -111,6 +112,9 @@ else if (processor != null) { if (lvt != null && lvt.getSig() != null) { buffer.append(GenericMain.getGenericCastTypeName(new GenericType(lvt.getSig()))).append(" "); } + else if (lvt != null) { + buffer.append(ExprProcessor.getCastTypeName(lvt.getVarType())).append(" "); + } else { buffer.append(ExprProcessor.getCastTypeName(getVarType())).append(" "); } @@ -231,7 +235,7 @@ public boolean match(MatchNode matchNode, MatchEngine engine) { public void setLVT(LVTVariable lvt) { this.lvt = lvt; - if (processor != null) { + if (processor != null && lvt != null) { processor.setVarType(new VarVersionPair(this), lvt.getVarType()); } } @@ -245,4 +249,13 @@ public String toString() { return lvt != null ? lvt.name : "var_" + index + "_" + version; } + @Override + public CheckTypesResult checkExprTypeBounds() { + CheckTypesResult checkExprTypeBounds = super.checkExprTypeBounds(); + if (lvt != null) { + checkExprTypeBounds.addMinTypeExprent(this, lvt.getVarType()); + } + return checkExprTypeBounds; + } + } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java index d6c746b..4a2cb8f 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java @@ -108,7 +108,12 @@ public int processExprent(Exprent exprent) { for (Exprent expr : lst) { if (expr.type == Exprent.EXPRENT_VAR) { - ((VarExprent)expr).setVarType(VarType.VARTYPE_UNKNOWN); + VarExprent ve = (VarExprent)expr; + if (ve.getLVT()!= null) { + ve.setVarType(ve.getLVT().getVarType()); + } else { + ve.setVarType(VarType.VARTYPE_UNKNOWN); + } } else if (expr.type == Exprent.EXPRENT_CONST) { ConstExprent constExpr = (ConstExprent)expr; From 1dfe3bcf3f09d6595a5dbd0bac11056ebad79b4e Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Thu, 1 Oct 2015 18:03:36 -0700 Subject: [PATCH 38/73] NPE protection in LVT loading. --- .../struct/attr/StructLocalVariableTableAttribute.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java index 771ec7a..a95870f 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java @@ -61,8 +61,13 @@ public void initContent(ConstantPool pool) throws IOException { } public void addLocalVariableTable(StructLocalVariableTableAttribute attr) { - lvt.mergeLVTs(attr.lvt); - attr.lvt = lvt; + if (lvt == null) { + lvt = attr.lvt; + } + else { + lvt.mergeLVTs(attr.lvt); + attr.lvt = lvt; + } } public Map> getMapVarNames() { From 8ea51a091a3d0fa64dbf8fd39d70391c7b7d5f80 Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Thu, 1 Oct 2015 18:04:09 -0700 Subject: [PATCH 39/73] Fix variable defintions in IfStatement's headers having wrong scope. --- .../modules/decompiler/vars/VarDefinitionHelper.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java index cc5e5e4..560328a 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java @@ -531,7 +531,7 @@ private void applyTypes(Exprent exprent, Map types) var.setLVT(lvt); } else { System.currentTimeMillis(); - System.out.println("null " + new VarVersionPair(var)); + //System.out.println("null " + new VarVersionPair(var)); } } } @@ -631,6 +631,9 @@ private VPPEntry mergeVars(Statement stat, Map parent, leaked_n.clear(); // Force no leaking at the end of if blocks // We may need to do this for Switches as well.. But havent run into that issue yet... } + else if (obj == ifst.getFirst()) { + leaked.putAll(leaked_n); //First is outside the scope so leak! + } } this_vars.putAll(leaked_n); } From de52ba808f928bd138c8cbf29baee707ecf469d3 Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Thu, 1 Oct 2015 20:00:53 -0700 Subject: [PATCH 40/73] Detect if the itterator is being used elseware before creating an enhanced for loop. --- .../modules/decompiler/MergeHelper.java | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java index 3172b5e..5ba4dcd 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java @@ -458,6 +458,14 @@ private static boolean matchForEach(DoStatement stat) { return false; } + InvocationExprent next = (InvocationExprent)getUncast(ass.getRight()); + InvocationExprent hnext = (InvocationExprent)getUncast(drillNots(stat.getConditionExprent())); + if (next.getInstance().type != Exprent.EXPRENT_VAR || + hnext.getInstance().type != Exprent.EXPRENT_VAR || + isVarReferenced((VarExprent)initDoExprent.getLeft(), stat, (VarExprent)next.getInstance(), (VarExprent)hnext.getInstance())) { + return false; + } + InvocationExprent holder = (InvocationExprent)((AssignmentExprent)initDoExprent).getRight(); holder.getInstance().addBytecodeOffsets(initDoExprent.bytecode); @@ -487,6 +495,12 @@ else if (stat.getLooptype() == DoStatement.LOOP_FOR) { return false; } + AssignmentExprent itr = (AssignmentExprent)stat.getInitExprent(); + InvocationExprent hnext = (InvocationExprent)exp; + if (itr.getLeft().type != Exprent.EXPRENT_VAR || hnext.getInstance().type != Exprent.EXPRENT_VAR) { + return false; + } + AssignmentExprent ass = (AssignmentExprent)stat.getIncExprent(); if (!isNextCall(ass.getRight()) || ass.getLeft().type != Exprent.EXPRENT_VAR) { @@ -495,6 +509,12 @@ else if (stat.getLooptype() == DoStatement.LOOP_FOR) { return false; } + InvocationExprent next = (InvocationExprent)getUncast(firstDoExprent.getRight()); + if (next.getInstance().type != Exprent.EXPRENT_VAR || + isVarReferenced((VarExprent)itr.getLeft(), stat, (VarExprent)next.getInstance(), (VarExprent)hnext.getInstance())) { + return false; + } + //Move the inc exprent back to the end of the body and remove the .next call Statement last = getLastDirectData(stat.getFirst()); InvocationExprent holder = (InvocationExprent)getUncast(((AssignmentExprent)stat.getInitExprent()).getRight()); @@ -511,6 +531,12 @@ else if (stat.getLooptype() == DoStatement.LOOP_FOR) { stat.setIncExprent(holder.getInstance()); } else { + InvocationExprent next = (InvocationExprent)getUncast(ass.getRight()); + if (next.getInstance().type != Exprent.EXPRENT_VAR || + isVarReferenced((VarExprent)itr.getLeft(), stat, (VarExprent)next.getInstance(), (VarExprent)hnext.getInstance())) { + return false; + } + InvocationExprent holder = (InvocationExprent)getUncast(((AssignmentExprent)stat.getInitExprent()).getRight()); holder.getInstance().addBytecodeOffsets(stat.getInitExprent().bytecode); @@ -579,6 +605,53 @@ else if (initDoExprent != null && initDoExprent.getRight().type == Exprent.EXPRE return true; } + private static boolean isVarReferenced(VarExprent var, Statement stat, VarExprent... whitelist) { + if (stat.getExprents() == null) { + for (Object obj : stat.getSequentialObjects()) { + if (obj instanceof Statement) { + if (isVarReferenced(var, (Statement)obj, whitelist)) { + return true; + } + } + else if (obj instanceof Exprent) { + if (isVarReferenced(var, (Exprent)obj, whitelist)) { + return true; + } + } + } + } + else { + for (Exprent exp : stat.getExprents()) { + if (isVarReferenced(var, exp, whitelist)) { + return true; + } + } + } + return false; + } + + private static boolean isVarReferenced(VarExprent target, Exprent exp, VarExprent... whitelist) { + List lst = exp.getAllExprents(true); + lst.add(exp); + for (Exprent ex : lst) { + if (ex != target && ex.type == Exprent.EXPRENT_VAR) { + VarExprent var = (VarExprent)ex; + if (var.getIndex() == target.getIndex() && var.getVersion() == target.getVersion()) { + boolean allowed = false; + for (VarExprent white : whitelist) { + if (var == white) { + allowed = true; + } + } + if (!allowed) { + return true; + } + } + } + } + return false; + } + private static boolean isType(Exprent exp, int type) { //This is just a helper macro, Wish java had real macros. return exp != null && exp.type == type; } From 950b975082f6252b0b3aedf6674f6af9b2951c33 Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Fri, 2 Oct 2015 01:32:57 -0700 Subject: [PATCH 41/73] NPE Protection in EnumProcessor --- src/org/jetbrains/java/decompiler/main/EnumProcessor.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/org/jetbrains/java/decompiler/main/EnumProcessor.java b/src/org/jetbrains/java/decompiler/main/EnumProcessor.java index 37a3384..8956e0e 100644 --- a/src/org/jetbrains/java/decompiler/main/EnumProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/EnumProcessor.java @@ -74,7 +74,9 @@ else if (CodeConstants.INIT_NAME.equals(name)) { // FIXME: move to a util class (see also InitializerProcessor) private static Statement findFirstData(Statement stat) { - + if (stat == null) { + return null; + } if (stat.getExprents() != null) { return stat; } From 280f466fb5135e0612b0afed08741a0925ee1936 Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Fri, 2 Oct 2015 01:34:53 -0700 Subject: [PATCH 42/73] Cleanup unwanted variables in synchronized headers. --- .../main/rels/MethodProcessorRunnable.java | 2 + .../modules/decompiler/MergeHelper.java | 54 ++------------ .../decompiler/SynchronizedHelper.java | 51 +++++++++++++ .../decompiler/vars/VarDefinitionHelper.java | 6 ++ .../java/decompiler/util/ExprentUtil.java | 71 +++++++++++++++++++ 5 files changed, 134 insertions(+), 50 deletions(-) create mode 100644 src/org/jetbrains/java/decompiler/modules/decompiler/SynchronizedHelper.java create mode 100644 src/org/jetbrains/java/decompiler/util/ExprentUtil.java diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java index 5844b93..a22d269 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java @@ -205,6 +205,8 @@ public static RootStatement codeToJava(StructMethod mt, VarProcessor varProc) th SecondaryFunctionsHelper.identifySecondaryFunctions(root); + SynchronizedHelper.cleanSynchronizedVar(root); + varProc.setVarDefinitions(root); // must be the last invocation, because it makes the statement structure inconsistent diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java index 5ba4dcd..5adf323 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java @@ -26,6 +26,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; import org.jetbrains.java.decompiler.modules.decompiler.stats.*; +import org.jetbrains.java.decompiler.util.ExprentUtil; import java.util.ArrayList; import java.util.List; @@ -462,7 +463,7 @@ private static boolean matchForEach(DoStatement stat) { InvocationExprent hnext = (InvocationExprent)getUncast(drillNots(stat.getConditionExprent())); if (next.getInstance().type != Exprent.EXPRENT_VAR || hnext.getInstance().type != Exprent.EXPRENT_VAR || - isVarReferenced((VarExprent)initDoExprent.getLeft(), stat, (VarExprent)next.getInstance(), (VarExprent)hnext.getInstance())) { + ExprentUtil.isVarReferenced((VarExprent)initDoExprent.getLeft(), stat, (VarExprent)next.getInstance(), (VarExprent)hnext.getInstance())) { return false; } @@ -511,7 +512,7 @@ else if (stat.getLooptype() == DoStatement.LOOP_FOR) { InvocationExprent next = (InvocationExprent)getUncast(firstDoExprent.getRight()); if (next.getInstance().type != Exprent.EXPRENT_VAR || - isVarReferenced((VarExprent)itr.getLeft(), stat, (VarExprent)next.getInstance(), (VarExprent)hnext.getInstance())) { + ExprentUtil.isVarReferenced((VarExprent)itr.getLeft(), stat, (VarExprent)next.getInstance(), (VarExprent)hnext.getInstance())) { return false; } @@ -533,7 +534,7 @@ else if (stat.getLooptype() == DoStatement.LOOP_FOR) { else { InvocationExprent next = (InvocationExprent)getUncast(ass.getRight()); if (next.getInstance().type != Exprent.EXPRENT_VAR || - isVarReferenced((VarExprent)itr.getLeft(), stat, (VarExprent)next.getInstance(), (VarExprent)hnext.getInstance())) { + ExprentUtil.isVarReferenced((VarExprent)itr.getLeft(), stat, (VarExprent)next.getInstance(), (VarExprent)hnext.getInstance())) { return false; } @@ -605,53 +606,6 @@ else if (initDoExprent != null && initDoExprent.getRight().type == Exprent.EXPRE return true; } - private static boolean isVarReferenced(VarExprent var, Statement stat, VarExprent... whitelist) { - if (stat.getExprents() == null) { - for (Object obj : stat.getSequentialObjects()) { - if (obj instanceof Statement) { - if (isVarReferenced(var, (Statement)obj, whitelist)) { - return true; - } - } - else if (obj instanceof Exprent) { - if (isVarReferenced(var, (Exprent)obj, whitelist)) { - return true; - } - } - } - } - else { - for (Exprent exp : stat.getExprents()) { - if (isVarReferenced(var, exp, whitelist)) { - return true; - } - } - } - return false; - } - - private static boolean isVarReferenced(VarExprent target, Exprent exp, VarExprent... whitelist) { - List lst = exp.getAllExprents(true); - lst.add(exp); - for (Exprent ex : lst) { - if (ex != target && ex.type == Exprent.EXPRENT_VAR) { - VarExprent var = (VarExprent)ex; - if (var.getIndex() == target.getIndex() && var.getVersion() == target.getVersion()) { - boolean allowed = false; - for (VarExprent white : whitelist) { - if (var == white) { - allowed = true; - } - } - if (!allowed) { - return true; - } - } - } - } - return false; - } - private static boolean isType(Exprent exp, int type) { //This is just a helper macro, Wish java had real macros. return exp != null && exp.type == type; } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/SynchronizedHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/SynchronizedHelper.java new file mode 100644 index 0000000..371fc5a --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/SynchronizedHelper.java @@ -0,0 +1,51 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.java.decompiler.modules.decompiler; + +import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.MonitorExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.SynchronizedStatement; +import org.jetbrains.java.decompiler.util.ExprentUtil; + +public class SynchronizedHelper { + public static void cleanSynchronizedVar(Statement stat) { + for (Statement st : stat.getStats()) { + cleanSynchronizedVar(st); + } + + if (stat.type == Statement.TYPE_SYNCRONIZED) { + SynchronizedStatement sync = (SynchronizedStatement)stat; + if (sync.getHeadexprent().type == Exprent.EXPRENT_MONITOR) { + MonitorExprent mon = (MonitorExprent)sync.getHeadexprent(); + for (Exprent e : sync.getFirst().getExprents()) { + if (e.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent ass = (AssignmentExprent)e; + if (ass.getLeft().type == Exprent.EXPRENT_VAR) { + VarExprent var = (VarExprent)ass.getLeft(); + if (ass.getRight().equals(mon.getValue()) && !ExprentUtil.isVarReferenced(var, stat.getParent())) { + sync.getFirst().getExprents().remove(e); + break; + } + } + } + } + } + } + } +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java index 560328a..e294648 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java @@ -635,6 +635,12 @@ else if (obj == ifst.getFirst()) { leaked.putAll(leaked_n); //First is outside the scope so leak! } } + else if (stat.type == Statement.TYPE_SWITCH || + stat.type == Statement.TYPE_SYNCRONIZED) { + if (obj == stat.getFirst()) { + leaked.putAll(leaked_n); //First is outside the scope so leak! + } + } this_vars.putAll(leaked_n); } } diff --git a/src/org/jetbrains/java/decompiler/util/ExprentUtil.java b/src/org/jetbrains/java/decompiler/util/ExprentUtil.java new file mode 100644 index 0000000..39ce7ff --- /dev/null +++ b/src/org/jetbrains/java/decompiler/util/ExprentUtil.java @@ -0,0 +1,71 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.java.decompiler.util; + +import java.util.List; + +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; + +public class ExprentUtil { + public static boolean isVarReferenced(VarExprent var, Statement stat, VarExprent... whitelist) { + if (stat.getExprents() == null) { + for (Object obj : stat.getSequentialObjects()) { + if (obj instanceof Statement) { + if (isVarReferenced(var, (Statement)obj, whitelist)) { + return true; + } + } + else if (obj instanceof Exprent) { + if (isVarReferenced(var, (Exprent)obj, whitelist)) { + return true; + } + } + } + } + else { + for (Exprent exp : stat.getExprents()) { + if (isVarReferenced(var, exp, whitelist)) { + return true; + } + } + } + return false; + } + + private static boolean isVarReferenced(VarExprent target, Exprent exp, VarExprent... whitelist) { + List lst = exp.getAllExprents(true); + lst.add(exp); + for (Exprent ex : lst) { + if (ex != target && ex.type == Exprent.EXPRENT_VAR) { + VarExprent var = (VarExprent)ex; + if (var.getIndex() == target.getIndex() && var.getVersion() == target.getVersion()) { + boolean allowed = false; + for (VarExprent white : whitelist) { + if (var == white) { + allowed = true; + } + } + if (!allowed) { + return true; + } + } + } + } + return false; + } +} From 19773bf44e7d8f1ddbf06fe60ede5677ad698e03 Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Fri, 2 Oct 2015 03:10:28 -0700 Subject: [PATCH 43/73] Include LVT info in VarExprent.copy --- .../modules/decompiler/exps/VarExprent.java | 1 + .../decompiler/vars/VarDefinitionHelper.java | 16 ++++++---------- .../modules/decompiler/vars/VarProcessor.java | 2 +- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java index 933b046..c85d889 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java @@ -82,6 +82,7 @@ public Exprent copy() { var.setVersion(version); var.setClassDef(classDef); var.setStack(stack); + var.setLVT(lvt); return var; } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java index e294648..88dae4d 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java @@ -260,22 +260,18 @@ else if (obj instanceof Exprent) { } private LVTVariable findLVT(int index, Exprent exp) { - VarExprent var = null; - - if (exp.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent ass = (AssignmentExprent)exp; - if (ass.getLeft().type == Exprent.EXPRENT_VAR) { - var = (VarExprent)ass.getLeft(); + for (Exprent e : exp.getAllExprents(false)) { + LVTVariable lvt = findLVT(index, e); + if (lvt != null) { + return lvt; } } - else if (exp.type == Exprent.EXPRENT_VAR) { - var = (VarExprent)exp; - } - if (var == null) { + if (exp.type != Exprent.EXPRENT_VAR) { return null; } + VarExprent var = (VarExprent)exp; return var.getIndex() == index ? var.getLVT() : null; } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java index bbf17ad..89f24f3 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java @@ -166,7 +166,7 @@ public LocalVariableTable getLVT() { public void findLVT(VarExprent varExprent, int bytecodeOffset) { LVTVariable var = this.lvt == null ? null : lvt.find(varExprent.getIndex(), bytecodeOffset); if (var != null) { - varExprent.setLVT(var); + varExprent.setLVT(var); } } From 89ea238257bbf10339409978ea26f982fd497090 Mon Sep 17 00:00:00 2001 From: cpw Date: Fri, 2 Oct 2015 21:02:44 -0400 Subject: [PATCH 44/73] Remove dowhile detection, and restore do loop collapsing code. Seems to eliminate a lot of derpy loop smashage. --- .../main/rels/MethodProcessorRunnable.java | 10 +++--- .../decompiler/EliminateLoopsHelper.java | 36 ++++++++++--------- .../modules/decompiler/MergeHelper.java | 2 +- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java index a22d269..ef7e97a 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java @@ -162,15 +162,17 @@ public static RootStatement codeToJava(StructMethod mt, VarProcessor varProc) th LabelHelper.cleanUpEdges(root); while (true) { + if (EliminateLoopsHelper.eliminateLoops(root, cl)) { + continue; + } MergeHelper.enhanceLoops(root); - + if (!IfHelper.mergeAllIfs(root)) { + break; + } if (LoopExtractHelper.extractLoops(root)) { continue; } - if (!IfHelper.mergeAllIfs(root)) { - break; - } } if (DecompilerContext.getOption(IFernflowerPreferences.IDEA_NOT_NULL_ANNOTATION)) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/EliminateLoopsHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/EliminateLoopsHelper.java index 16f7da0..823e561 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/EliminateLoopsHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/EliminateLoopsHelper.java @@ -17,32 +17,34 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.struct.StructClass; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; public class EliminateLoopsHelper { - // public static boolean eliminateLoops(Statement root) { - // - // boolean ret = eliminateLoopsRec(root); - // - // if(ret) { - // SequenceHelper.condenseSequences(root); - // - // HashSet setReorderedIfs = new HashSet(); - // - // SimplifyExprentsHelper sehelper = new SimplifyExprentsHelper(false); - // while(sehelper.simplifyStackVarsStatement(root, setReorderedIfs, null)) { - // SequenceHelper.condenseSequences(root); - // } - // } - // - // return ret; - // } + public static boolean eliminateLoops(Statement root, StructClass cl) { + + boolean ret = eliminateLoopsRec(root); + + if(ret) { + SequenceHelper.condenseSequences(root); + + HashSet setReorderedIfs = new HashSet(); + + SimplifyExprentsHelper sehelper = new SimplifyExprentsHelper(false); + while(sehelper.simplifyStackVarsStatement(root, setReorderedIfs, null,cl)) { + SequenceHelper.condenseSequences(root); + } + } + + return ret; + } private static boolean eliminateLoopsRec(Statement stat) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java index 5adf323..0e7f1ab 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java @@ -74,7 +74,7 @@ private static boolean enhanceLoop(DoStatement stat) { } else { // identify a do{}while loop - matchDoWhile(stat); +// matchDoWhile(stat); } break; From 34a7253e7b3997dda29ae47f63151c976c9275a4 Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Sat, 3 Oct 2015 22:54:34 -0700 Subject: [PATCH 45/73] Move LoopExtract to before MergeHelper and add more debug information to printMethod. --- .../main/rels/MethodProcessorRunnable.java | 46 +++++++++++++------ .../modules/decompiler/DomHelper.java | 13 ++---- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java index ef7e97a..eca3413 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java @@ -19,6 +19,7 @@ import org.jetbrains.java.decompiler.code.InstructionSequence; import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph; import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; @@ -27,6 +28,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.deobfuscator.ExceptionDeobfuscator; import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.IfExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; import org.jetbrains.java.decompiler.modules.decompiler.stats.DummyExitStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; @@ -125,11 +127,11 @@ public static RootStatement codeToJava(StructMethod mt, VarProcessor varProc) th DecompilerContext.getLogger().writeMessage("Heavily obfuscated exception ranges found!", IFernflowerLogger.Severity.WARN); } - RootStatement root = DomHelper.parseGraph(graph); + RootStatement root = DomHelper.parseGraph(graph, mt); FinallyProcessor fProc = new FinallyProcessor(varProc); while (fProc.iterateGraph(mt, root, graph)) { - root = DomHelper.parseGraph(graph); + root = DomHelper.parseGraph(graph, mt); } // remove synchronized exception handler @@ -162,17 +164,19 @@ public static RootStatement codeToJava(StructMethod mt, VarProcessor varProc) th LabelHelper.cleanUpEdges(root); while (true) { - if (EliminateLoopsHelper.eliminateLoops(root, cl)) { - continue; - } - MergeHelper.enhanceLoops(root); - if (!IfHelper.mergeAllIfs(root)) { - break; - } + if (EliminateLoopsHelper.eliminateLoops(root, cl)) { + continue; + } + if (LoopExtractHelper.extractLoops(root)) { continue; } + MergeHelper.enhanceLoops(root); + + if (!IfHelper.mergeAllIfs(root)) { + break; + } } if (DecompilerContext.getOption(IFernflowerPreferences.IDEA_NOT_NULL_ANNOTATION)) { @@ -230,7 +234,7 @@ public boolean isFinished() { return finished; } - public static void printMethod(RootStatement root, String name, VarProcessor varProc) { + public static void printMethod(Statement root, String name, VarProcessor varProc) { System.out.println(name + " {"); if (root == null || root.getSequentialObjects() == null) { System.out.println("}"); @@ -243,7 +247,9 @@ public static void printMethod(RootStatement root, String name, VarProcessor var System.out.println(" " + obj.getClass().getSimpleName()); } } - printStatement(root.getDummyExit(), " ",varProc); + if (root instanceof RootStatement) { + printStatement(((RootStatement)root).getDummyExit(), " ",varProc); + } System.out.println("}"); } @@ -256,6 +262,9 @@ public static void getOffset(Statement st, BitSet values) { } } else { for (Object obj : st.getSequentialObjects()) { + if (obj == null) { + continue; + } if (obj instanceof Statement) { getOffset((Statement)obj, values); } else if (obj instanceof Exprent) { @@ -273,7 +282,9 @@ private static void printStatement(Statement statement, String indent, VarProces int start = values.nextSetBit(0); int end = values.length()-1; - System.out.println(indent + statement.type + ": (" + start + ", " + end + ") " + statement.getClass().getSimpleName()); + System.out.println(indent + "{" + statement.type + "}:" + statement.id + " (" + start + ", " + end + ") " + statement.getClass().getSimpleName()); + for (StatEdge edge : statement.getAllSuccessorEdges()) + System.out.println(indent + " Dest: " + edge.getDestination()); if (statement.getExprents() != null) { for(Exprent exp : statement.getExprents()) { @@ -282,6 +293,9 @@ private static void printStatement(Statement statement, String indent, VarProces } indent += " "; for (Object obj : statement.getSequentialObjects()) { + if (obj == null) { + continue; + } if (obj instanceof Statement) { printStatement((Statement)obj, indent,varProc); } else if (obj instanceof Exprent) { @@ -302,13 +316,15 @@ private static String printExprent(String indent, Exprent exp, VarProcessor varP if (exp instanceof VarExprent) { VarExprent varExprent = (VarExprent)exp; int currindex = varExprent.getIndex(); - int origindex = varProc.getRemapped(currindex); + int origindex = varProc == null ? -2 : varProc.getRemapped(currindex); sb.append("[").append(currindex).append(":").append(origindex).append(", ").append(varExprent.isStack()).append("]"); - if (varProc.getLVT() != null) + if (varProc != null && varProc.getLVT() != null) sb.append(varProc.getLVT().getCandidates(origindex)); } else if (exp instanceof AssignmentExprent) { - AssignmentExprent assignmentExprent = (AssignmentExprent)exp; + AssignmentExprent assignmentExprent = (AssignmentExprent)exp; sb.append("{").append(printExprent(" ",assignmentExprent.getLeft(),varProc)).append(" =").append(printExprent(" ",assignmentExprent.getRight(),varProc)).append("}"); + } else if (exp instanceof IfExprent) { + sb.append(" ").append(exp.toJava(0, new BytecodeMappingTracer())); } return sb.toString(); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java index c93be38..67d16d5 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java @@ -20,9 +20,12 @@ import org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; +import org.jetbrains.java.decompiler.main.rels.MethodProcessorRunnable; import org.jetbrains.java.decompiler.modules.decompiler.decompose.FastExtendedPostdominanceHelper; import org.jetbrains.java.decompiler.modules.decompiler.deobfuscator.IrreducibleCFGDeobfuscator; import org.jetbrains.java.decompiler.modules.decompiler.stats.*; +import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.util.DotExporter; import org.jetbrains.java.decompiler.util.FastFixedSetFactory; import org.jetbrains.java.decompiler.util.FastFixedSetFactory.FastFixedSet; import org.jetbrains.java.decompiler.util.InterpreterUtil; @@ -207,17 +210,11 @@ public int compare(Integer o1, Integer o2) { return ret; } - public static RootStatement parseGraph(ControlFlowGraph graph) { + public static RootStatement parseGraph(ControlFlowGraph graph, StructMethod mt) { RootStatement root = graphToStatement(graph); - if (!processStatement(root, new HashMap>())) { - - // try { - // DotExporter.toDotFile(root.getFirst().getStats().get(13), new File("c:\\Temp\\stat1.dot")); - // } catch (Exception ex) { - // ex.printStackTrace(); - // } + DotExporter.toDotFile(graph, mt, "parseGraphFail", true); throw new RuntimeException("parsing failure!"); } From 751b2227a9b40757f0c01008882263e0c4acbd1d Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Sat, 3 Oct 2015 22:55:00 -0700 Subject: [PATCH 46/73] Loosen up LoopExtractHelper to include break edges that can be extracted. --- .../modules/decompiler/LoopExtractHelper.java | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java index d5c9e8f..93bc21d 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Set; @@ -79,21 +80,27 @@ private static boolean extractLoop(DoStatement stat) { return false; } + List stats = new ArrayList(); for (StatEdge edge : stat.getLabelEdges()) { if (edge.getType() != StatEdge.TYPE_CONTINUE && edge.getDestination().type != Statement.TYPE_DUMMYEXIT) { - return false; + if (edge.getType() == StatEdge.TYPE_BREAK && isExternStatement(stat, edge.getSource(), edge.getSource())) { + stats.add(edge.getSource()); + } + else { + return false; + } } } - if (!extractLastIf(stat)) { - return extractFirstIf(stat); + if (!extractLastIf(stat, stats)) { + return extractFirstIf(stat, stats); } else { return true; } } - private static boolean extractLastIf(DoStatement stat) { + private static boolean extractLastIf(DoStatement stat, List stats) { // search for an if condition at the end of the loop Statement last = stat.getFirst(); @@ -114,6 +121,11 @@ private static boolean extractLastIf(DoStatement stat) { if (set.isEmpty()) { // no direct continues in a do{}while loop if (isExternStatement(stat, ifstat, ifstat)) { + for (Statement s : stats) { + if (!ifstat.containsStatement(s)) { + return false; + } + } extractIfBlock(stat, lastif); return true; } @@ -124,7 +136,7 @@ private static boolean extractLastIf(DoStatement stat) { return false; } - private static boolean extractFirstIf(DoStatement stat) { + private static boolean extractFirstIf(DoStatement stat, List stats) { // search for an if condition at the entrance of the loop Statement first = stat.getFirst(); @@ -142,6 +154,11 @@ private static boolean extractFirstIf(DoStatement stat) { Statement ifstat = firstif.getIfstat(); if (isExternStatement(stat, ifstat, ifstat)) { + for (Statement s : stats) { + if (!ifstat.containsStatement(s)) { + return false; + } + } extractIfBlock(stat, firstif); return true; } From 3be7e213dcd0be08189ca0207b54c600a0ff4602 Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Sun, 4 Oct 2015 02:10:44 -0700 Subject: [PATCH 47/73] Hack to prevent extracted ifs from being inlined again in infinite loop. MC decompiles much better! --- .../main/rels/MethodProcessorRunnable.java | 2 -- .../decompiler/modules/decompiler/ExitHelper.java | 2 +- .../decompiler/InlineSingleBlockHelper.java | 5 ++++- .../modules/decompiler/LoopExtractHelper.java | 15 +++++++++++++-- .../decompiler/modules/decompiler/StatEdge.java | 2 ++ .../decompiler/stats/SequenceStatement.java | 1 - 6 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java index eca3413..62d5297 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java @@ -33,14 +33,12 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.DummyExitStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; -import org.jetbrains.java.decompiler.modules.decompiler.vars.LVTVariable; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructMethod; import java.io.IOException; import java.util.BitSet; -import java.util.List; public class MethodProcessorRunnable implements Runnable { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java index d467cdc..df2cb4a 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java @@ -206,7 +206,7 @@ private static Statement isExitEdge(StatEdge edge) { Statement dest = edge.getDestination(); if (edge.getType() == StatEdge.TYPE_BREAK && dest.type == Statement.TYPE_BASICBLOCK - && edge.explicit && (edge.labeled || isOnlyEdge(edge))) { + && edge.explicit && (edge.labeled || isOnlyEdge(edge)) && edge.canInline) { List data = dest.getExprents(); if (data != null && data.size() == 1) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java index adeda02..1c468c8 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java @@ -132,7 +132,10 @@ private static boolean isInlineable(SequenceStatement seq, int index) { StatEdge edge = lst.get(0); if (sameCatchRanges(edge)) { - if (edge.explicit) { + if (!edge.canInline) { + return false; //Dirty hack, but lets do it! + } + else if (edge.explicit) { return true; } else { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java index 93bc21d..f09dd3c 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java @@ -92,8 +92,14 @@ private static boolean extractLoop(DoStatement stat) { } } - if (!extractLastIf(stat, stats)) { - return extractFirstIf(stat, stats); + if (stats.size() > 0) { // In this case prioritize first to help the Loop enhancer. + if (stat.getParent().getStats().getLast() != stat){ + return false; + } + } + + if (!extractFirstIf(stat, stats)) { + return extractLastIf(stat, stats); } else { return true; @@ -219,5 +225,10 @@ private static void extractIfBlock(DoStatement loop, IfStatement ifstat) { loop.addPredecessor(edge); } } + + List link = target.getPredecessorEdges(StatEdge.TYPE_BREAK); + if (link.size() == 1) { + link.get(0).canInline = false; + } } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/StatEdge.java b/src/org/jetbrains/java/decompiler/modules/decompiler/StatEdge.java index 26d427b..7902069 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/StatEdge.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/StatEdge.java @@ -52,6 +52,8 @@ public class StatEdge { public boolean explicit = true; + public boolean canInline = true; + public StatEdge(int type, Statement source, Statement destination, Statement closure) { this(type, source, destination); this.closure = closure; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java index 81d31a5..7d2ee43 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java @@ -20,7 +20,6 @@ import org.jetbrains.java.decompiler.modules.decompiler.DecHelper; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; -import org.jetbrains.java.decompiler.modules.decompiler.vars.StartEndPair; import java.util.Arrays; import java.util.List; From 7dec35b37bbeb3c785e142980718e799d25c618a Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Sun, 4 Oct 2015 04:22:34 -0700 Subject: [PATCH 48/73] Include ForEach source vars in itteration, allows for remapping in merge. --- .../java/decompiler/modules/decompiler/stats/DoStatement.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java index 36cd7fb..acc74a6 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java @@ -164,6 +164,7 @@ public List getSequentialObjects() { break; case LOOP_FOREACH: lst.add(getInitExprent()); + lst.add(getIncExprent()); } lst.add(first); From fd8b2a3bfada12e9d371230cb329f64ab801544e Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Sun, 4 Oct 2015 04:23:24 -0700 Subject: [PATCH 49/73] Re-write ForEach detection to be before For detection. Fixes issues where incrementors would be added back in incorrectly. --- .../modules/decompiler/MergeHelper.java | 159 ++++++------------ 1 file changed, 56 insertions(+), 103 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java index 0e7f1ab..f354f06 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java @@ -20,6 +20,7 @@ import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.modules.decompiler.exps.ArrayExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.IfExprent; @@ -67,10 +68,9 @@ private static boolean enhanceLoop(DoStatement stat) { // identify a while loop if (matchWhile(stat)) { - // identify a for loop - subtype of while - matchFor(stat); - // identify for each loop, - matchForEach(stat); + if (!matchForEach(stat)) { + matchFor(stat); + } } else { // identify a do{}while loop @@ -79,8 +79,9 @@ private static boolean enhanceLoop(DoStatement stat) { break; case DoStatement.LOOP_WHILE: - matchFor(stat); - matchForEach(stat); + if (!matchForEach(stat)) { + matchFor(stat); + } } return (stat.getLooptype() != oldloop); @@ -402,8 +403,10 @@ private static boolean matchFor(DoStatement stat) { } private static boolean matchForEach(DoStatement stat) { - AssignmentExprent firstDoExprent = null, initDoExprent = null, initCopyExprent = null; - Statement firstData, preData = null; + AssignmentExprent firstDoExprent = null; + AssignmentExprent[] initExprents = new AssignmentExprent[3]; + Statement firstData = null, preData = null, lastData = null; + Exprent lastExprent = null; // search for an initializing exprent Statement current = stat; @@ -421,14 +424,13 @@ private static boolean matchForEach(DoStatement stat) { preData = current.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD).get(0); preData = getLastDirectData(preData); if (preData != null && !preData.getExprents().isEmpty()) { - Exprent exprent = preData.getExprents().get(preData.getExprents().size() - 1); - if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { - initDoExprent = (AssignmentExprent)exprent; - if (preData.getExprents().size() >= 2) { - exprent = preData.getExprents().get(preData.getExprents().size() - 2); - if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { - initCopyExprent = (AssignmentExprent)exprent; - } + int size = preData.getExprents().size(); + for (int x = 0; x < initExprents.length; x++) { + if (size > x) { + Exprent exprent = preData.getExprents().get(size - 1 - x); + if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { + initExprents[x] = (AssignmentExprent)exprent; + } } } } @@ -444,10 +446,14 @@ private static boolean matchForEach(DoStatement stat) { if (firstData != null && firstData.getExprents().get(0).type == Exprent.EXPRENT_ASSIGNMENT) { firstDoExprent = (AssignmentExprent)firstData.getExprents().get(0); } + lastData = getLastDirectData(stat.getFirst()); + if (lastData != null && !lastData.getExprents().isEmpty()) { + lastExprent = lastData.getExprents().get(lastData.getExprents().size() - 1); + } - if (stat.getLooptype() == DoStatement.LOOP_WHILE && initDoExprent != null && firstDoExprent != null) { - if (initDoExprent.type == Exprent.EXPRENT_ASSIGNMENT && - isIteratorCall(((AssignmentExprent)initDoExprent).getRight())) { + if (stat.getLooptype() == DoStatement.LOOP_WHILE && initExprents[0] != null && firstDoExprent != null) { + if (initExprents[0].type == Exprent.EXPRENT_ASSIGNMENT && + isIteratorCall(((AssignmentExprent)initExprents[0]).getRight())) { if (!isHasNextCall(drillNots(stat.getConditionExprent())) || firstDoExprent.type != Exprent.EXPRENT_ASSIGNMENT) { @@ -463,13 +469,13 @@ private static boolean matchForEach(DoStatement stat) { InvocationExprent hnext = (InvocationExprent)getUncast(drillNots(stat.getConditionExprent())); if (next.getInstance().type != Exprent.EXPRENT_VAR || hnext.getInstance().type != Exprent.EXPRENT_VAR || - ExprentUtil.isVarReferenced((VarExprent)initDoExprent.getLeft(), stat, (VarExprent)next.getInstance(), (VarExprent)hnext.getInstance())) { + ExprentUtil.isVarReferenced((VarExprent)initExprents[0].getLeft(), stat, (VarExprent)next.getInstance(), (VarExprent)hnext.getInstance())) { return false; } - InvocationExprent holder = (InvocationExprent)((AssignmentExprent)initDoExprent).getRight(); + InvocationExprent holder = (InvocationExprent)((AssignmentExprent)initExprents[0]).getRight(); - holder.getInstance().addBytecodeOffsets(initDoExprent.bytecode); + holder.getInstance().addBytecodeOffsets(initExprents[0].bytecode); ass.getLeft().addBytecodeOffsets(firstDoExprent.bytecode); if (stat.getIncExprent() != null) { holder.getInstance().addBytecodeOffsets(stat.getIncExprent().bytecode); @@ -481,88 +487,31 @@ private static boolean matchForEach(DoStatement stat) { stat.setLooptype(DoStatement.LOOP_FOREACH); stat.setInitExprent(ass.getLeft()); stat.setIncExprent(holder.getInstance()); - preData.getExprents().remove(initDoExprent); + preData.getExprents().remove(initExprents[0]); firstData.getExprents().remove(firstDoExprent); + return true; } - } - else if (stat.getLooptype() == DoStatement.LOOP_FOR) { - if (isType(stat.getInitExprent(), Exprent.EXPRENT_ASSIGNMENT) && - isIteratorCall(((AssignmentExprent)stat.getInitExprent()).getRight()) && - isType(stat.getConditionExprent(), Exprent.EXPRENT_FUNCTION)) { - - Exprent exp = drillNots(stat.getConditionExprent()); - if (!isHasNextCall(exp) || - !isType(stat.getIncExprent(), Exprent.EXPRENT_ASSIGNMENT)) { + else if (initExprents[0] != null && initExprents[1] != null && firstDoExprent != null) { + if (firstDoExprent.getRight().type != Exprent.EXPRENT_ARRAY || firstDoExprent.getLeft().type != Exprent.EXPRENT_VAR) { return false; } - AssignmentExprent itr = (AssignmentExprent)stat.getInitExprent(); - InvocationExprent hnext = (InvocationExprent)exp; - if (itr.getLeft().type != Exprent.EXPRENT_VAR || hnext.getInstance().type != Exprent.EXPRENT_VAR) { + if (lastExprent == null || lastExprent.type != Exprent.EXPRENT_FUNCTION) { return false; } - AssignmentExprent ass = (AssignmentExprent)stat.getIncExprent(); - if (!isNextCall(ass.getRight()) || ass.getLeft().type != Exprent.EXPRENT_VAR) { - - if (firstDoExprent == null || !isNextCall(firstDoExprent.getRight()) || - firstDoExprent.getLeft().type != Exprent.EXPRENT_VAR) { - return false; - } - - InvocationExprent next = (InvocationExprent)getUncast(firstDoExprent.getRight()); - if (next.getInstance().type != Exprent.EXPRENT_VAR || - ExprentUtil.isVarReferenced((VarExprent)itr.getLeft(), stat, (VarExprent)next.getInstance(), (VarExprent)hnext.getInstance())) { - return false; - } - - //Move the inc exprent back to the end of the body and remove the .next call - Statement last = getLastDirectData(stat.getFirst()); - InvocationExprent holder = (InvocationExprent)getUncast(((AssignmentExprent)stat.getInitExprent()).getRight()); - - firstData.getExprents().remove(firstDoExprent); - last.getExprents().add(stat.getIncExprent()); - - firstDoExprent.getLeft().addBytecodeOffsets(stat.getInitExprent().bytecode); - firstDoExprent.getLeft().addBytecodeOffsets(stat.getIncExprent().bytecode); - firstDoExprent.getLeft().addBytecodeOffsets(firstDoExprent.bytecode); - - stat.setLooptype(DoStatement.LOOP_FOREACH); - stat.setInitExprent(firstDoExprent.getLeft()); - stat.setIncExprent(holder.getInstance()); - } - else { - InvocationExprent next = (InvocationExprent)getUncast(ass.getRight()); - if (next.getInstance().type != Exprent.EXPRENT_VAR || - ExprentUtil.isVarReferenced((VarExprent)itr.getLeft(), stat, (VarExprent)next.getInstance(), (VarExprent)hnext.getInstance())) { - return false; - } - - InvocationExprent holder = (InvocationExprent)getUncast(((AssignmentExprent)stat.getInitExprent()).getRight()); - - holder.getInstance().addBytecodeOffsets(stat.getInitExprent().bytecode); - ass.getLeft().addBytecodeOffsets(stat.getIncExprent().bytecode); - - stat.setLooptype(DoStatement.LOOP_FOREACH); - stat.setInitExprent(ass.getLeft()); - stat.setIncExprent(holder.getInstance()); - } - } - else if (initDoExprent != null && initDoExprent.getRight().type == Exprent.EXPRENT_FUNCTION) { - if (firstDoExprent == null || - firstDoExprent.getRight().type != Exprent.EXPRENT_ARRAY || - firstDoExprent.getLeft().type != Exprent.EXPRENT_VAR || - !isType(stat.getIncExprent(), Exprent.EXPRENT_FUNCTION) || - !isType(stat.getInitExprent(), Exprent.EXPRENT_ASSIGNMENT)) { + if (initExprents[0].getRight().type != Exprent.EXPRENT_CONST || + initExprents[1].getRight().type != Exprent.EXPRENT_FUNCTION) { return false; } - FunctionExprent funcRight = (FunctionExprent)initDoExprent.getRight(); - FunctionExprent funcInc = (FunctionExprent)stat.getIncExprent(); - ArrayExprent arr = (ArrayExprent)firstDoExprent.getRight(); + FunctionExprent funcRight = (FunctionExprent)initExprents[1].getRight(); + FunctionExprent funcInc = (FunctionExprent)lastExprent; + ArrayExprent arr = (ArrayExprent)firstDoExprent.getRight(); + int incType = funcInc.getFuncType(); if (funcRight.getFuncType() != FunctionExprent.FUNCTION_ARRAY_LENGTH || - (funcInc.getFuncType() != FunctionExprent.FUNCTION_PPI && funcInc.getFuncType() != FunctionExprent.FUNCTION_IPP) || + (incType != FunctionExprent.FUNCTION_PPI && incType != FunctionExprent.FUNCTION_IPP) || arr.getIndex().type != Exprent.EXPRENT_VAR || arr.getArray().type != Exprent.EXPRENT_VAR) { return false; @@ -577,33 +526,37 @@ else if (initDoExprent != null && initDoExprent.getRight().type == Exprent.EXPRE return false; } - funcRight.getLstOperands().get(0).addBytecodeOffsets(initDoExprent.bytecode); - funcRight.getLstOperands().get(0).addBytecodeOffsets(stat.getIncExprent().bytecode); + funcRight.getLstOperands().get(0).addBytecodeOffsets(initExprents[0].bytecode); + funcRight.getLstOperands().get(0).addBytecodeOffsets(initExprents[1].bytecode); + funcRight.getLstOperands().get(0).addBytecodeOffsets(lastExprent.bytecode); firstDoExprent.getLeft().addBytecodeOffsets(firstDoExprent.bytecode); - firstDoExprent.getLeft().addBytecodeOffsets(stat.getInitExprent().bytecode); + firstDoExprent.getLeft().addBytecodeOffsets(initExprents[0].bytecode); stat.setLooptype(DoStatement.LOOP_FOREACH); stat.setInitExprent(firstDoExprent.getLeft()); stat.setIncExprent(funcRight.getLstOperands().get(0)); - preData.getExprents().remove(initDoExprent); + preData.getExprents().remove(initExprents[0]); + preData.getExprents().remove(initExprents[1]); firstData.getExprents().remove(firstDoExprent); + lastData.getExprents().remove(lastExprent); - - if (initCopyExprent != null && initCopyExprent.getLeft().type == Exprent.EXPRENT_VAR) { - VarExprent copy = (VarExprent)initCopyExprent.getLeft(); + if (initExprents[2] != null && initExprents[2].getLeft().type == Exprent.EXPRENT_VAR) { + VarExprent copy = (VarExprent)initExprents[2].getLeft(); if (copy.getIndex() == array.getIndex() && copy.getVersion() == array.getVersion()) { - preData.getExprents().remove(initCopyExprent); - initCopyExprent.getRight().addBytecodeOffsets(initCopyExprent.bytecode); - initCopyExprent.getRight().addBytecodeOffsets(stat.getIncExprent().bytecode); - stat.setIncExprent(initCopyExprent.getRight()); + preData.getExprents().remove(initExprents[2]); + initExprents[2].getRight().addBytecodeOffsets(initExprents[2].bytecode); + initExprents[2].getRight().addBytecodeOffsets(stat.getIncExprent().bytecode); + stat.setIncExprent(initExprents[2].getRight()); } } + + return true; } } //cleanEmptyStatements(stat, firstData); //TODO: Look into this and see what it does... - return true; + return false; } private static boolean isType(Exprent exp, int type) { //This is just a helper macro, Wish java had real macros. From bf26fb1390f668dcadb5dc15cdf9cbfc12b84d81 Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Sun, 4 Oct 2015 16:52:33 -0700 Subject: [PATCH 50/73] Propogate LVTs to inner classes. Fix improper var merging in var handlers. --- .../main/rels/NestedClassProcessor.java | 37 +++++++++++-- .../modules/decompiler/vars/LVTVariable.java | 8 ++- .../decompiler/vars/VarDefinitionHelper.java | 53 ++++++++++++++----- .../modules/decompiler/vars/VarProcessor.java | 13 ++++- .../decompiler/vars/VarVersionsProcessor.java | 2 +- 5 files changed, 90 insertions(+), 23 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java b/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java index c6eda1b..3378549 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java @@ -28,6 +28,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.modules.decompiler.vars.LVTVariable; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; import org.jetbrains.java.decompiler.struct.StructClass; @@ -454,6 +455,8 @@ private static void insertLocalVars(final ClassNode parent, final ClassNode chil HashMap mapNewNames = new HashMap(); // local var types HashMap mapNewTypes = new HashMap(); + // local var table entries + HashMap mapNewLVTs = new HashMap(); final HashMap mapParamsToNewVars = new HashMap(); if (meth.signatureFields != null) { @@ -469,15 +472,17 @@ private static void insertLocalVars(final ClassNode parent, final ClassNode chil String varname = null; VarType vartype = null; + LVTVariable varlvt = null; if (child.type != ClassNode.CLASS_MEMBER) { varname = encmeth.varproc.getVarName(paar); vartype = encmeth.varproc.getVarType(paar); + varlvt = encmeth.varproc.getVarLVT(paar); encmeth.varproc.setVarFinal(paar, VarTypeProcessor.VAR_EXPLICIT_FINAL); } - if (paar.var == -1 || "this".equals(varname)) { + if (paar.var == -1 || "this".equals(varname) || (varlvt != null && "this".equals(varlvt.name))) { if (parent.simpleName == null) { // anonymous enclosing class, no access to this varname = VarExprent.VAR_NAMELESS_ENCLOSURE; @@ -485,11 +490,15 @@ private static void insertLocalVars(final ClassNode parent, final ClassNode chil else { varname = parent.simpleName + ".this"; } + if (varlvt != null) { + varlvt = varlvt.rename(varname); + } meth.varproc.getThisVars().put(newvar, parent.classStruct.qualifiedName); } mapNewNames.put(newvar, varname); mapNewTypes.put(newvar, vartype); + mapNewLVTs.put(newvar, varlvt); } varindex += md.params[index++].stackSize; } @@ -507,6 +516,7 @@ private static void insertLocalVars(final ClassNode parent, final ClassNode chil String varname = null; VarType vartype = null; + LVTVariable varlvt = null; if (clnode.type != ClassNode.CLASS_MEMBER) { @@ -514,11 +524,12 @@ private static void insertLocalVars(final ClassNode parent, final ClassNode chil varname = enclosing_method.varproc.getVarName(entr.getValue()); vartype = enclosing_method.varproc.getVarType(entr.getValue()); + varlvt = enclosing_method.varproc.getVarLVT(entr.getValue()); enclosing_method.varproc.setVarFinal(entr.getValue(), VarTypeProcessor.VAR_EXPLICIT_FINAL); } - if (entr.getValue().var == -1 || "this".equals(varname)) { + if (entr.getValue().var == -1 || "this".equals(varname) || (varlvt != null && "this".equals(varlvt.name))) { if (clnode.parent.simpleName == null) { // anonymous enclosing class, no access to this varname = VarExprent.VAR_NAMELESS_ENCLOSURE; @@ -526,11 +537,15 @@ private static void insertLocalVars(final ClassNode parent, final ClassNode chil else { varname = clnode.parent.simpleName + ".this"; } + if (varlvt != null) { + varlvt = varlvt.rename(varname); + } meth.varproc.getThisVars().put(newvar, clnode.parent.classStruct.qualifiedName); } mapNewNames.put(newvar, varname); mapNewTypes.put(newvar, vartype); + mapNewLVTs.put(newvar, varlvt); // hide synthetic field if (clnode == child) { // fields higher up the chain were already handled with their classes @@ -549,11 +564,15 @@ private static void insertLocalVars(final ClassNode parent, final ClassNode chil for (Entry entr : mapNewNames.entrySet()) { VarVersionPair varpaar = entr.getKey(); VarType vartype = mapNewTypes.get(varpaar); + LVTVariable varlvt = mapNewLVTs.get(varpaar); meth.varproc.setVarName(varpaar, entr.getValue()); if (vartype != null) { meth.varproc.setVarType(varpaar, vartype); } + if (varlvt != null) { + meth.varproc.setVarLVT(varpaar, varlvt); + } } DirectGraph graph = meth.getOrBuildGraph(); @@ -602,7 +621,12 @@ private Exprent replaceExprent(Exprent exprent) { if (mapParamsToNewVars.containsKey(varindex)) { VarVersionPair newvar = mapParamsToNewVars.get(varindex); meth.varproc.getExternalVars().add(newvar); - return new VarExprent(newvar.var, meth.varproc.getVarType(newvar), meth.varproc); + VarExprent ret = new VarExprent(newvar.var, meth.varproc.getVarType(newvar), meth.varproc); + LVTVariable lvt = meth.varproc.getVarLVT(newvar); + if (lvt != null) { + ret.setLVT(lvt); + } + return ret; } } else if (exprent.type == Exprent.EXPRENT_FIELD) { @@ -616,7 +640,12 @@ else if (exprent.type == Exprent.EXPRENT_FIELD) { // mapFieldsToNewVars.containsKey(keyField)) { VarVersionPair newvar = mapFieldsToNewVars.get(keyField); meth.varproc.getExternalVars().add(newvar); - return new VarExprent(newvar.var, meth.varproc.getVarType(newvar), meth.varproc); + VarExprent ret = new VarExprent(newvar.var, meth.varproc.getVarType(newvar), meth.varproc); + LVTVariable lvt = meth.varproc.getVarLVT(newvar); + if (lvt != null) { + ret.setLVT(lvt); + } + return ret; } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LVTVariable.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LVTVariable.java index 209d3e0..cb376d9 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LVTVariable.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/LVTVariable.java @@ -83,7 +83,11 @@ public String getSig() { return sig; } -public VarType getVarType() { + public VarType getVarType() { return new VarType(desc); -} + } + + public LVTVariable rename(String newName) { + return new LVTVariable(newName, desc, start, end, index, isLVTT); + } } \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java index 88dae4d..0427e3b 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java @@ -440,6 +440,10 @@ private void propogateLVTs(Statement stat) { findTypes(stat, types); + for (Entry e : types.entrySet()) { + varproc.setVarLVT(e.getKey(), e.getValue()); + } + applyTypes(stat, types); } @@ -470,23 +474,24 @@ else if (obj instanceof Exprent) { } private void findTypes(Exprent exp, Map types) { - VarExprent var = null; + List lst = exp.getAllExprents(true); + lst.add(exp); - if (exp.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent ass = (AssignmentExprent)exp; - if (ass.getLeft().type == Exprent.EXPRENT_VAR) { - var = (VarExprent)ass.getLeft(); + for (Exprent exprent : lst) { + if (exprent.type == Exprent.EXPRENT_VAR) { + VarExprent var = (VarExprent)exprent; + if (var.getLVT() == null) { + continue; + } + VarVersionPair ver = new VarVersionPair(var); + if (var.isDefinition()) { + types.put(ver, var.getLVT()); + } + else if (!types.containsKey(ver)) { + types.put(ver, var.getLVT()); + } } } - else if (exp.type == Exprent.EXPRENT_VAR) { - var = (VarExprent)exp; - } - - if (var == null || !var.isDefinition()) { - return; - } - - types.put(new VarVersionPair(var), var.getLVT()); } @@ -495,6 +500,10 @@ private void applyTypes(Statement stat, Map types) return; } + for (Exprent exp : stat.getVarDefinitions()) { + applyTypes(exp, types); + } + if (stat.getExprents() == null) { for (Object obj : stat.getSequentialObjects()) { if (obj instanceof Statement) { @@ -637,6 +646,10 @@ else if (stat.type == Statement.TYPE_SWITCH || leaked.putAll(leaked_n); //First is outside the scope so leak! } } + else if (stat.type == Statement.TYPE_TRYCATCH || + stat.type == Statement.TYPE_CATCHALL) { + leaked_n.clear(); // Catches can't leak anything mwhahahahah! + } this_vars.putAll(leaked_n); } } @@ -732,6 +745,18 @@ else if (obj instanceof Exprent) { } success |= remapped; } + if (success) { + Iterator itr = stat.getVarDefinitions().iterator(); + while (itr.hasNext()) { + Exprent exp = itr.next(); + if (exp.type == Exprent.EXPRENT_VAR) { + VarExprent var = (VarExprent)exp; + if (from.var == var.getIndex() && from.version == var.getVersion()) { + itr.remove(); + } + } + } + } return success; } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java index 89f24f3..04ee48f 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java @@ -29,6 +29,7 @@ public class VarProcessor { private Map mapVarNames = new HashMap(); + private Map mapVarLVTs = new HashMap(); private VarVersionsProcessor varVersions; private final Map thisVars = new HashMap(); private final Set externalVars = new HashSet(); @@ -127,7 +128,7 @@ public VarType getVarType(VarVersionPair pair) { public void setVarType(VarVersionPair pair, VarType type) { if (varVersions != null) { - varVersions.setVarType(pair, type); + varVersions.setVarType(pair, type); } } @@ -185,5 +186,13 @@ public void copyVarInfo(VarVersionPair from, VarVersionPair to) { public VarVersionsProcessor getVarVersions() { return varVersions; -} + } + + public void setVarLVT(VarVersionPair var, LVTVariable lvt) { + mapVarLVTs.put(var, lvt); + } + + public LVTVariable getVarLVT(VarVersionPair var) { + return mapVarLVTs.get(var); + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java index d201157..a1acd19 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java @@ -323,5 +323,5 @@ public Map getMapOriginalVarIndices() { } public VarTypeProcessor getTypeProcessor() { return typeProcessor; -} + } } From 08cf2eed89abfcfed52fbcaac9ee4c4b172b465a Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Sun, 4 Oct 2015 18:14:24 -0700 Subject: [PATCH 51/73] Fix issue where For loops would be enhanced to ForEach incorrectly. --- .../java/decompiler/modules/decompiler/MergeHelper.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java index f354f06..11c7c59 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java @@ -501,10 +501,12 @@ else if (initExprents[0] != null && initExprents[1] != null && firstDoExprent != } if (initExprents[0].getRight().type != Exprent.EXPRENT_CONST || - initExprents[1].getRight().type != Exprent.EXPRENT_FUNCTION) { + initExprents[1].getRight().type != Exprent.EXPRENT_FUNCTION || + stat.getConditionExprent().type != Exprent.EXPRENT_FUNCTION) { return false; } + //FunctionExprent funcCond = (FunctionExprent)drillNots(stat.getConditionExprent()); //TODO: Verify this is counter < copy.length FunctionExprent funcRight = (FunctionExprent)initExprents[1].getRight(); FunctionExprent funcInc = (FunctionExprent)lastExprent; ArrayExprent arr = (ArrayExprent)firstDoExprent.getRight(); @@ -525,6 +527,9 @@ else if (initExprents[0] != null && initExprents[1] != null && firstDoExprent != counter.getVersion() != index.getVersion()) { return false; } + if (ExprentUtil.isVarReferenced(counter, stat.getFirst(), index)) { + return false; + } funcRight.getLstOperands().get(0).addBytecodeOffsets(initExprents[0].bytecode); funcRight.getLstOperands().get(0).addBytecodeOffsets(initExprents[1].bytecode); From af6a44bafd270347d61481d9d3b97ef3c263a42b Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Sun, 4 Oct 2015 19:29:55 -0700 Subject: [PATCH 52/73] Fix constructor arguments for non-static inner classes with generic signatures. --- .../java/decompiler/main/ClassWriter.java | 8 +++++++- .../struct/gen/generics/GenericType.java | 20 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index 3e5789d..daf3444 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -725,6 +725,12 @@ else if (CodeConstants.CLINIT_NAME.equals(name)) { int index = isEnum && init ? 3 : thisVar ? 1 : 0; boolean hasDescriptor = descriptor != null; int start = isEnum && init && !hasDescriptor ? 2 : 0; + + if (init && hasDescriptor && !isEnum && + ((node.access & CodeConstants.ACC_STATIC) == 0) && node.type == ClassNode.CLASS_MEMBER) { + index++; //Enclosing class + } + int params = hasDescriptor ? descriptor.params.size() : md.params.length; for (int i = start; i < params; i++) { if (hasDescriptor || (signFields == null || signFields.get(i) == null)) { @@ -787,7 +793,7 @@ else if (CodeConstants.CLINIT_NAME.equals(name)) { paramCount++; } - index += md.params[i].stackSize; + index += hasDescriptor ? descriptor.params.get(i).stackSize : md.params[i].stackSize; } buffer.append(')'); diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java index d3b3896..9305bf9 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java @@ -31,6 +31,7 @@ public class GenericType { public final int type; public final int arrayDim; public final String value; + public final int stackSize; private final List enclosingClasses = new ArrayList(); private final List arguments = new ArrayList(); @@ -40,6 +41,7 @@ public GenericType(int type, int arrayDim, String value) { this.type = type; this.arrayDim = arrayDim; this.value = value; + this.stackSize = getStackSize(type, arrayDim); } public GenericType(String signature) { @@ -102,6 +104,7 @@ public GenericType(String signature) { this.type = type; this.arrayDim = arrayDim; this.value = value; + this.stackSize = getStackSize(type, arrayDim); } private static String getNextClassSignature(String value) { @@ -209,6 +212,23 @@ public static String getNextType(String value) { return value.substring(0, index + 1); } + private static int getStackSize(int type, int arrayDim) { + if (arrayDim > 0) { + return 1; + } + + switch (type) { + case CodeConstants.TYPE_DOUBLE: + case CodeConstants.TYPE_LONG: + return 2; + case CodeConstants.TYPE_VOID: + case CodeConstants.TYPE_GROUP2EMPTY: + return 0; + default: + return 1; + } + } + public GenericType decreaseArrayDim() { assert arrayDim > 0 : this; return new GenericType(type, arrayDim - 1, value); From e083cb151d05afd2e06431fb24443e381c03b361 Mon Sep 17 00:00:00 2001 From: cpw Date: Mon, 5 Oct 2015 00:12:38 -0400 Subject: [PATCH 53/73] Return generic typevar casting support --- .../jetbrains/java/decompiler/main/ClassWriter.java | 5 ++++- .../decompiler/modules/decompiler/ExprProcessor.java | 7 +++---- .../modules/decompiler/exps/ExitExprent.java | 12 +++++++++++- .../java/decompiler/struct/gen/MethodDescriptor.java | 5 +++++ .../java/decompiler/struct/gen/VarType.java | 7 ++++++- 5 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index daf3444..404265f 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -607,6 +607,7 @@ private boolean methodToJava(ClassNode node, StructMethod mt, TextBuffer buffer, boolean clinit = false, init = false, dinit = false; MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); + DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR, md); int flags = mt.getAccessFlags(); if ((flags & CodeConstants.ACC_NATIVE) != 0) { @@ -686,7 +687,9 @@ else if (CodeConstants.CLINIT_NAME.equals(name)) { } } } - + if (descriptor != null) { + md.addGenericDescriptor(descriptor); + } boolean throwsExceptions = false; int paramCount = 0; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java index 7b94e5b..1abd093 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java @@ -731,10 +731,9 @@ else if (tp == CodeConstants.TYPE_VOID) { return "void"; } else if (tp == CodeConstants.TYPE_OBJECT) { - //if (type.signatureDebugGens != null) { - // GenericType gen = new GenericType(type.signatureDebugGens); - // return GenericMain.getGenericCastTypeName(gen); - //} + if (type.genericType != null) { + return GenericMain.getGenericCastTypeName(type.genericType); + } String ret = buildJavaClassName(type.value); if (getShort) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java index fd1491f..bbf1458 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java @@ -24,7 +24,10 @@ import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; import org.jetbrains.java.decompiler.struct.attr.StructExceptionsAttribute; +import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.struct.gen.generics.GenericMain; +import org.jetbrains.java.decompiler.struct.gen.generics.GenericType; import org.jetbrains.java.decompiler.struct.match.MatchEngine; import org.jetbrains.java.decompiler.struct.match.MatchNode; import org.jetbrains.java.decompiler.struct.match.IMatchable.MatchProperties; @@ -81,13 +84,20 @@ public List getAllExprents() { @Override public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { tracer.addMapping(bytecode); + MethodDescriptor md = (MethodDescriptor) DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR); if (exitType == EXIT_RETURN) { TextBuffer buffer = new TextBuffer(); if (retType.type != CodeConstants.TYPE_VOID) { + VarType ret = retType; + boolean force = false; + if (md.genericInfo != null && md.genericInfo.ret != null) { + ret = new VarType(md.genericInfo.ret); + force = true; + } buffer.append(" "); - ExprProcessor.getCastedExprent(value, retType, buffer, indent, false, tracer); + ExprProcessor.getCastedExprent(value, ret, buffer, indent, false, force, tracer); } return buffer.prepend("return"); diff --git a/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java b/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java index 2465382..0ab555f 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java @@ -16,6 +16,7 @@ package org.jetbrains.java.decompiler.struct.gen; import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.struct.gen.generics.GenericMethodDescriptor; import java.util.ArrayList; import java.util.Arrays; @@ -25,6 +26,7 @@ public class MethodDescriptor { public final VarType[] params; public final VarType ret; + public GenericMethodDescriptor genericInfo; private MethodDescriptor(VarType[] params, VarType ret) { this.params = params; @@ -78,6 +80,9 @@ public static MethodDescriptor parseDescriptor(String descriptor) { return new MethodDescriptor(params, ret); } + public void addGenericDescriptor(GenericMethodDescriptor desc) { + this.genericInfo = desc; + } public String buildNewDescriptor(NewClassNameBuilder builder) { boolean updated = false; diff --git a/src/org/jetbrains/java/decompiler/struct/gen/VarType.java b/src/org/jetbrains/java/decompiler/struct/gen/VarType.java index 1eb177f..45fd289 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/VarType.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/VarType.java @@ -16,6 +16,7 @@ package org.jetbrains.java.decompiler.struct.gen; import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.struct.gen.generics.GenericType; import org.jetbrains.java.decompiler.util.InterpreterUtil; public class VarType { // TODO: optimize switch @@ -46,7 +47,7 @@ public class VarType { // TODO: optimize switch public final int typeFamily; public final int stackSize; public final boolean falseBoolean; - + public GenericType genericType; public VarType(int type) { this(type, 0); } @@ -55,6 +56,10 @@ public VarType(int type, int arrayDim) { this(type, arrayDim, getChar(type)); } + public VarType(GenericType type) { + this(VARTYPE_OBJECT.type); + this.genericType = type; + } public VarType(int type, int arrayDim, String value) { this(type, arrayDim, value, getFamily(type, arrayDim), getStackSize(type, arrayDim), false); } From a88b1c06f60850be429ebdba20e5e6ba507936bb Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Fri, 9 Oct 2015 13:22:13 -0700 Subject: [PATCH 54/73] Add new decompiler option -iec Include_Entire_Classpath which will read the current JVM's classpath and parse all libraries for metadata. --- .../java/decompiler/main/Fernflower.java | 29 +++++++++++++++++++ .../main/extern/IFernflowerPreferences.java | 3 ++ 2 files changed, 32 insertions(+) diff --git a/src/org/jetbrains/java/decompiler/main/Fernflower.java b/src/org/jetbrains/java/decompiler/main/Fernflower.java index e355ee9..65733ac 100644 --- a/src/org/jetbrains/java/decompiler/main/Fernflower.java +++ b/src/org/jetbrains/java/decompiler/main/Fernflower.java @@ -19,6 +19,7 @@ import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider; import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; +import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger.Severity; import org.jetbrains.java.decompiler.main.extern.IResultSaver; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.modules.renamer.IdentifierConverter; @@ -27,7 +28,10 @@ import org.jetbrains.java.decompiler.struct.StructContext; import org.jetbrains.java.decompiler.struct.lazy.LazyLoader; +import java.io.File; +import java.util.HashSet; import java.util.Map; +import java.util.Set; public class Fernflower implements IDecompiledData { @@ -39,6 +43,10 @@ public Fernflower(IBytecodeProvider provider, IResultSaver saver, Map found = new HashSet(); + String[] props = { System.getProperty("java.class.path"), System.getProperty("sun.boot.class.path") }; + for (String prop : props) { + if (prop == null) + continue; + + for (final String path : prop.split(File.pathSeparator)) { + File file = new File(path); + if (found.contains(file.getAbsolutePath())) + continue; + + if (file.exists()) { + DecompilerContext.getLogger().writeMessage("Adding File to context from classpath: " + file, Severity.INFO); + structContext.addSpace(file, false); + found.add(file.getAbsolutePath()); + } + } + } + } } diff --git a/src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java b/src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java index 31871c5..79dd8e8 100644 --- a/src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java +++ b/src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java @@ -60,6 +60,8 @@ public interface IFernflowerPreferences { String LINE_SEPARATOR_WIN = "\r\n"; String LINE_SEPARATOR_LIN = "\n"; + String INCLUDE_ENTIRE_CLASSPATH = "iec"; + Map DEFAULTS = Collections.unmodifiableMap(new HashMap() {{ put(REMOVE_BRIDGE, "1"); put(REMOVE_SYNTHETIC, "0"); @@ -93,5 +95,6 @@ public interface IFernflowerPreferences { put(BANNER, ""); put(UNIT_TEST_MODE, "0"); put(DUMP_ORIGINAL_LINES, "0"); + put(INCLUDE_ENTIRE_CLASSPATH, "0"); }}); } From 676f9ba6584df07224d713698aca89eae71b44e4 Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Fri, 9 Oct 2015 13:29:20 -0700 Subject: [PATCH 55/73] Rework GenericType to be subclass of VarType and contain all data needed to be treated as such. --- .../java/decompiler/main/ClassWriter.java | 148 ++++--------- .../modules/decompiler/ExprProcessor.java | 7 +- .../modules/decompiler/exps/ExitExprent.java | 6 +- .../modules/decompiler/exps/NewExprent.java | 20 +- .../modules/decompiler/exps/VarExprent.java | 2 +- .../java/decompiler/struct/StructClass.java | 21 ++ .../java/decompiler/struct/StructField.java | 21 ++ .../java/decompiler/struct/StructMethod.java | 18 +- .../java/decompiler/struct/gen/VarType.java | 15 +- .../gen/generics/GenericClassDescriptor.java | 8 +- .../gen/generics/GenericFieldDescriptor.java | 4 +- .../struct/gen/generics/GenericMain.java | 82 ++----- .../gen/generics/GenericMethodDescriptor.java | 10 +- .../struct/gen/generics/GenericType.java | 204 ++++++++++++------ 14 files changed, 288 insertions(+), 278 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index 404265f..4ec7fe9 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -342,13 +342,7 @@ else if (isInterface) { buffer.append("class "); } - GenericClassDescriptor descriptor = null; - if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { - StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)cl.getAttributes().getWithKey("Signature"); - if (attr != null) { - descriptor = GenericMain.parseClassSignature(attr.getSignature()); - } - } + GenericClassDescriptor descriptor = cl.getSignature(); buffer.append(node.simpleName); @@ -362,12 +356,7 @@ else if (isInterface) { VarType supertype = new VarType(cl.superClass.getString(), true); if (!VarType.VARTYPE_OBJECT.equals(supertype)) { buffer.append("extends "); - if (descriptor != null) { - buffer.append(GenericMain.getGenericCastTypeName(descriptor.superclass)); - } - else { - buffer.append(ExprProcessor.getCastTypeName(supertype)); - } + buffer.append(ExprProcessor.getCastTypeName(descriptor == null ? supertype : descriptor.superclass)); buffer.append(' '); } } @@ -380,12 +369,8 @@ else if (isInterface) { if (i > 0) { buffer.append(", "); } - if (descriptor != null) { - buffer.append(GenericMain.getGenericCastTypeName(descriptor.superinterfaces.get(i))); - } - else { - buffer.append(ExprProcessor.getCastTypeName(new VarType(cl.getInterface(i), true))); - } + VarType iface = descriptor == null ? new VarType(cl.getInterface(i), true) : descriptor.superinterfaces.get(i); + buffer.append(ExprProcessor.getCastTypeName(iface)); } buffer.append(' '); } @@ -423,21 +408,10 @@ private void fieldToJava(ClassWrapper wrapper, StructClass cl, StructField fd, T VarType fieldType = new VarType(fd.getDescriptor(), false); - GenericFieldDescriptor descriptor = null; - if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { - StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)fd.getAttributes().getWithKey("Signature"); - if (attr != null) { - descriptor = GenericMain.parseFieldSignature(attr.getSignature()); - } - } + GenericFieldDescriptor descriptor = fd.getSignature(); if (!isEnum) { - if (descriptor != null) { - buffer.append(GenericMain.getGenericCastTypeName(descriptor.type)); - } - else { - buffer.append(ExprProcessor.getCastTypeName(fieldType)); - } + buffer.append(ExprProcessor.getCastTypeName(descriptor == null ? fieldType : descriptor.type)); buffer.append(' '); } @@ -662,33 +636,25 @@ else if (CodeConstants.CLINIT_NAME.equals(name)) { clinit = true; } - GenericMethodDescriptor descriptor = null; - if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { - StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)mt.getAttributes().getWithKey("Signature"); - if (attr != null) { - descriptor = GenericMain.parseMethodSignature(attr.getSignature()); - if (descriptor != null) { - int actualParams = md.params.length; - List sigFields = methodWrapper.signatureFields; - if (sigFields != null) { - actualParams = 0; - for (VarVersionPair field : methodWrapper.signatureFields) { - if (field == null) { - actualParams++; - } - } - } - else if (isEnum && init) actualParams -= 2; - if (actualParams != descriptor.params.size()) { - String message = "Inconsistent generic signature in method " + mt.getName() + " " + mt.getDescriptor() + " in " + cl.qualifiedName; - DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN); - descriptor = null; + GenericMethodDescriptor descriptor = mt.getSignature(); + if (descriptor != null) { + int actualParams = md.params.length; + List sigFields = methodWrapper.signatureFields; + if (sigFields != null) { + actualParams = 0; + for (VarVersionPair field : methodWrapper.signatureFields) { + if (field == null) { + actualParams++; } } } - } - if (descriptor != null) { - md.addGenericDescriptor(descriptor); + else if (isEnum && init) actualParams -= 2; + if (actualParams != descriptor.params.size()) { + String message = "Inconsistent generic signature in method " + mt.getName() + " " + mt.getDescriptor() + " in " + cl.qualifiedName; + DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN); + descriptor = null; + } + md.addGenericDescriptor(descriptor); } boolean throwsExceptions = false; int paramCount = 0; @@ -702,12 +668,7 @@ else if (CodeConstants.CLINIT_NAME.equals(name)) { } if (!init) { - if (descriptor != null) { - buffer.append(GenericMain.getGenericCastTypeName(descriptor.ret)); - } - else { - buffer.append(ExprProcessor.getCastTypeName(md.ret)); - } + buffer.append(ExprProcessor.getCastTypeName(descriptor == null ? md.ret : descriptor.ret)); buffer.append(' '); } @@ -747,45 +708,23 @@ else if (CodeConstants.CLINIT_NAME.equals(name)) { buffer.append("final "); } - if (descriptor != null) { - GenericType parameterType = descriptor.params.get(i); - - boolean isVarArg = (i == lastVisibleParameterIndex && mt.hasModifier(CodeConstants.ACC_VARARGS) && parameterType.arrayDim > 0); - if (isVarArg) { - parameterType = parameterType.decreaseArrayDim(); - } - - String typeName = GenericMain.getGenericCastTypeName(parameterType); - if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeName) && - DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { - typeName = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); - } - - buffer.append(typeName); + VarType parameterType = descriptor == null ? md.params[i] : descriptor.params.get(i); - if (isVarArg) { - buffer.append("..."); - } + boolean isVarArg = (i == lastVisibleParameterIndex && mt.hasModifier(CodeConstants.ACC_VARARGS) && parameterType.arrayDim > 0); + if (isVarArg) { + parameterType = parameterType.decreaseArrayDim(); } - else { - VarType parameterType = md.params[i]; - boolean isVarArg = (i == lastVisibleParameterIndex && mt.hasModifier(CodeConstants.ACC_VARARGS) && parameterType.arrayDim > 0); - if (isVarArg) { - parameterType = parameterType.decreaseArrayDim(); - } - - String typeName = ExprProcessor.getCastTypeName(parameterType); - if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeName) && - DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { - typeName = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); - } + String typeName = ExprProcessor.getCastTypeName(parameterType); + if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeName) && + DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { + typeName = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); + } - buffer.append(typeName); + buffer.append(typeName); - if (isVarArg) { - buffer.append("..."); - } + if (isVarArg) { + buffer.append("..."); } buffer.append(' '); @@ -810,14 +749,11 @@ else if (CodeConstants.CLINIT_NAME.equals(name)) { if (i > 0) { buffer.append(", "); } + VarType type = new VarType(attr.getExcClassname(i, cl.getPool()), true); if (descriptor != null && !descriptor.exceptions.isEmpty()) { - GenericType type = descriptor.exceptions.get(i); - buffer.append(GenericMain.getGenericCastTypeName(type)); - } - else { - VarType type = new VarType(attr.getExcClassname(i, cl.getPool()), true); - buffer.append(ExprProcessor.getCastTypeName(type)); + type = descriptor.exceptions.get(i); } + buffer.append(ExprProcessor.getCastTypeName(type)); } } } @@ -1080,7 +1016,7 @@ private static void appendModifiers(TextBuffer buffer, int flags, int allowed, b } } - private static void appendTypeParameters(TextBuffer buffer, List parameters, List> bounds) { + private static void appendTypeParameters(TextBuffer buffer, List parameters, List> bounds) { buffer.append('<'); for (int i = 0; i < parameters.size(); i++) { @@ -1090,13 +1026,13 @@ private static void appendTypeParameters(TextBuffer buffer, List paramet buffer.append(parameters.get(i)); - List parameterBounds = bounds.get(i); + List parameterBounds = bounds.get(i); if (parameterBounds.size() > 1 || !"java/lang/Object".equals(parameterBounds.get(0).value)) { buffer.append(" extends "); - buffer.append(GenericMain.getGenericCastTypeName(parameterBounds.get(0))); + buffer.append(ExprProcessor.getCastTypeName(parameterBounds.get(0))); for (int j = 1; j < parameterBounds.size(); j++) { buffer.append(" & "); - buffer.append(GenericMain.getGenericCastTypeName(parameterBounds.get(j))); + buffer.append(ExprProcessor.getCastTypeName(parameterBounds.get(j))); } } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java index 1abd093..162d9cb 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java @@ -730,9 +730,12 @@ else if (tp == CodeConstants.TYPE_NULL) { else if (tp == CodeConstants.TYPE_VOID) { return "void"; } + else if (tp == CodeConstants.TYPE_GENVAR && type.isGeneric()) { + return type.value; + } else if (tp == CodeConstants.TYPE_OBJECT) { - if (type.genericType != null) { - return GenericMain.getGenericCastTypeName(type.genericType); + if (type.isGeneric()) { + return ((GenericType)type).getCastName(); } String ret = buildJavaClassName(type.value); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java index bbf1458..3b5f0ba 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java @@ -26,11 +26,8 @@ import org.jetbrains.java.decompiler.struct.attr.StructExceptionsAttribute; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; -import org.jetbrains.java.decompiler.struct.gen.generics.GenericMain; -import org.jetbrains.java.decompiler.struct.gen.generics.GenericType; import org.jetbrains.java.decompiler.struct.match.MatchEngine; import org.jetbrains.java.decompiler.struct.match.MatchNode; -import org.jetbrains.java.decompiler.struct.match.IMatchable.MatchProperties; import org.jetbrains.java.decompiler.util.InterpreterUtil; import java.util.ArrayList; @@ -93,8 +90,7 @@ public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { VarType ret = retType; boolean force = false; if (md.genericInfo != null && md.genericInfo.ret != null) { - ret = new VarType(md.genericInfo.ret); - force = true; + ret = md.genericInfo.ret; } buffer.append(" "); ExprProcessor.getCastedExprent(value, ret, buffer, indent, false, force, tracer); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java index 1e87201..56e6771 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java @@ -238,19 +238,17 @@ public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { } } - if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { - StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)child.getWrapper().getClassStruct().getAttributes().getWithKey("Signature"); - if (attr != null) { - GenericClassDescriptor descriptor = GenericMain.parseClassSignature(attr.getSignature()); - // Anon classes can only be a child to one type. So either the first interface or the super class - if (descriptor.superinterfaces.size() > 0) { - typename = GenericMain.getGenericCastTypeName(descriptor.superinterfaces.get(0)); - } - else { - typename = GenericMain.getGenericCastTypeName(descriptor.superclass); - } + GenericClassDescriptor descriptor = child.getWrapper().getClassStruct().getSignature(); + if (descriptor != null) { + // Anon classes can only be a child to one type. So either the first interface or the super class + if (descriptor.superinterfaces.size() > 0) { + typename = ExprProcessor.getCastTypeName(descriptor.superinterfaces.get(0)); + } + else { + typename = ExprProcessor.getCastTypeName(descriptor.superclass); } } + buf.prepend("new " + typename); if (enclosing != null) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java index c85d889..4784e03 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java @@ -111,7 +111,7 @@ else if (processor != null) { buffer.append("final "); } if (lvt != null && lvt.getSig() != null) { - buffer.append(GenericMain.getGenericCastTypeName(new GenericType(lvt.getSig()))).append(" "); + buffer.append(ExprProcessor.getCastTypeName(GenericType.parse(lvt.getSig()))).append(" "); } else if (lvt != null) { buffer.append(ExprProcessor.getCastTypeName(lvt.getVarType())).append(" "); diff --git a/src/org/jetbrains/java/decompiler/struct/StructClass.java b/src/org/jetbrains/java/decompiler/struct/StructClass.java index cb24ff6..c0683fc 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructClass.java +++ b/src/org/jetbrains/java/decompiler/struct/StructClass.java @@ -16,8 +16,14 @@ package org.jetbrains.java.decompiler.struct; import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; +import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; +import org.jetbrains.java.decompiler.struct.attr.StructGenericSignatureAttribute; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; +import org.jetbrains.java.decompiler.struct.gen.generics.GenericClassDescriptor; +import org.jetbrains.java.decompiler.struct.gen.generics.GenericMain; import org.jetbrains.java.decompiler.struct.lazy.LazyLoader; import org.jetbrains.java.decompiler.util.DataInputFullStream; import org.jetbrains.java.decompiler.util.InterpreterUtil; @@ -58,6 +64,7 @@ public class StructClass extends StructMember { private final String[] interfaceNames; private final VBStyleCollection fields; private final VBStyleCollection methods; + private GenericClassDescriptor signature = null; private ConstantPool pool; @@ -192,4 +199,18 @@ public int getBytecodeVersion() { return CodeConstants.BYTECODE_JAVA_LE_4; } + + @Override + protected StructGeneralAttribute readAttribute(DataInputFullStream in, ConstantPool pool, String name) throws IOException { + StructGeneralAttribute attribute = super.readAttribute(in, pool, name); + if ("Signature".equals(name) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { + StructGenericSignatureAttribute signature = (StructGenericSignatureAttribute)attribute; + this.signature = GenericMain.parseClassSignature(signature.getSignature()); + } + return attribute; + } + + public GenericClassDescriptor getSignature() { + return signature; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/StructField.java b/src/org/jetbrains/java/decompiler/struct/StructField.java index 796d77f..cde19f1 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructField.java +++ b/src/org/jetbrains/java/decompiler/struct/StructField.java @@ -15,7 +15,13 @@ */ package org.jetbrains.java.decompiler.struct; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; +import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; +import org.jetbrains.java.decompiler.struct.attr.StructGenericSignatureAttribute; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import org.jetbrains.java.decompiler.struct.gen.generics.GenericFieldDescriptor; +import org.jetbrains.java.decompiler.struct.gen.generics.GenericMain; import org.jetbrains.java.decompiler.util.DataInputFullStream; import java.io.IOException; @@ -33,6 +39,7 @@ public class StructField extends StructMember { private final String name; private final String descriptor; + private GenericFieldDescriptor signature; public StructField(DataInputFullStream in, StructClass clStruct) throws IOException { @@ -55,4 +62,18 @@ public String getName() { public String getDescriptor() { return descriptor; } + + public GenericFieldDescriptor getSignature() { + return signature; + } + + @Override + protected StructGeneralAttribute readAttribute(DataInputFullStream in, ConstantPool pool, String name) throws IOException { + StructGeneralAttribute attribute = super.readAttribute(in, pool, name); + if ("Signature".equals(name) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { + StructGenericSignatureAttribute signature = (StructGenericSignatureAttribute)attribute; + this.signature = GenericMain.parseFieldSignature(signature.getSignature()); + } + return attribute; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/StructMethod.java b/src/org/jetbrains/java/decompiler/struct/StructMethod.java index 2b5249f..8f0da2b 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructMethod.java +++ b/src/org/jetbrains/java/decompiler/struct/StructMethod.java @@ -16,8 +16,13 @@ package org.jetbrains.java.decompiler.struct; import org.jetbrains.java.decompiler.code.*; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; +import org.jetbrains.java.decompiler.struct.attr.StructGenericSignatureAttribute; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import org.jetbrains.java.decompiler.struct.gen.generics.GenericMain; +import org.jetbrains.java.decompiler.struct.gen.generics.GenericMethodDescriptor; import org.jetbrains.java.decompiler.util.DataInputFullStream; import org.jetbrains.java.decompiler.util.VBStyleCollection; @@ -46,6 +51,7 @@ public class StructMethod extends StructMember { private final StructClass classStruct; private final String name; private final String descriptor; + private GenericMethodDescriptor signature; private boolean containsCode = false; private int localVariables = 0; @@ -99,7 +105,13 @@ protected StructGeneralAttribute readAttribute(DataInputFullStream in, ConstantP return null; } - return super.readAttribute(in, pool, name); + StructGeneralAttribute attribute = super.readAttribute(in, pool, name); + if ("Signature".equals(name) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { + StructGenericSignatureAttribute signature = (StructGenericSignatureAttribute)attribute; + this.signature = GenericMain.parseMethodSignature(signature.getSignature()); + } + + return attribute; } public void expandData() throws IOException { @@ -389,4 +401,8 @@ public int getLocalVariables() { public InstructionSequence getInstructionSequence() { return seq; } + + public GenericMethodDescriptor getSignature() { + return signature; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/gen/VarType.java b/src/org/jetbrains/java/decompiler/struct/gen/VarType.java index 45fd289..1b0df9d 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/VarType.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/VarType.java @@ -47,7 +47,6 @@ public class VarType { // TODO: optimize switch public final int typeFamily; public final int stackSize; public final boolean falseBoolean; - public GenericType genericType; public VarType(int type) { this(type, 0); } @@ -56,15 +55,11 @@ public VarType(int type, int arrayDim) { this(type, arrayDim, getChar(type)); } - public VarType(GenericType type) { - this(VARTYPE_OBJECT.type); - this.genericType = type; - } public VarType(int type, int arrayDim, String value) { this(type, arrayDim, value, getFamily(type, arrayDim), getStackSize(type, arrayDim), false); } - private VarType(int type, int arrayDim, String value, int typeFamily, int stackSize, boolean falseBoolean) { + protected VarType(int type, int arrayDim, String value, int typeFamily, int stackSize, boolean falseBoolean) { this.type = type; this.arrayDim = arrayDim; this.value = value; @@ -156,7 +151,7 @@ private static String getChar(int type) { } } - private static int getStackSize(int type, int arrayDim) { + protected static int getStackSize(int type, int arrayDim) { if (arrayDim > 0) { return 1; } @@ -173,7 +168,7 @@ private static int getStackSize(int type, int arrayDim) { } } - private static int getFamily(int type, int arrayDim) { + protected static int getFamily(int type, int arrayDim) { if (arrayDim > 0) { return CodeConstants.TYPE_FAMILY_OBJECT; } @@ -303,6 +298,10 @@ public String toString() { return res.toString(); } + public boolean isGeneric() { + return false; + } + // type1 and type2 must not be null public static VarType getCommonMinType(VarType type1, VarType type2) { if (type1.type == CodeConstants.TYPE_BOOLEAN && type2.type == CodeConstants.TYPE_BOOLEAN) { // special case booleans diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericClassDescriptor.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericClassDescriptor.java index 2ff2954..9418bb8 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericClassDescriptor.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericClassDescriptor.java @@ -18,13 +18,15 @@ import java.util.ArrayList; import java.util.List; +import org.jetbrains.java.decompiler.struct.gen.VarType; + public class GenericClassDescriptor { - public GenericType superclass; + public VarType superclass; - public final List superinterfaces = new ArrayList(); + public final List superinterfaces = new ArrayList(); public final List fparameters = new ArrayList(); - public final List> fbounds = new ArrayList>(); + public final List> fbounds = new ArrayList>(); } diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericFieldDescriptor.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericFieldDescriptor.java index 598d17b..9857f60 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericFieldDescriptor.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericFieldDescriptor.java @@ -15,7 +15,9 @@ */ package org.jetbrains.java.decompiler.struct.gen.generics; +import org.jetbrains.java.decompiler.struct.gen.VarType; + public class GenericFieldDescriptor { - public GenericType type; + public VarType type; } diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMain.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMain.java index 0a3f9d3..cffd008 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMain.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMain.java @@ -18,6 +18,8 @@ import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; +import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; +import org.jetbrains.java.decompiler.struct.gen.VarType; import java.util.ArrayList; import java.util.List; @@ -43,12 +45,12 @@ public static GenericClassDescriptor parseClassSignature(String signature) { signature = parseFormalParameters(signature, descriptor.fparameters, descriptor.fbounds); String superCl = GenericType.getNextType(signature); - descriptor.superclass = new GenericType(superCl); + descriptor.superclass = GenericType.parse(superCl); signature = signature.substring(superCl.length()); while (signature.length() > 0) { String superIf = GenericType.getNextType(signature); - descriptor.superinterfaces.add(new GenericType(superIf)); + descriptor.superinterfaces.add(GenericType.parse(superIf)); signature = signature.substring(superIf.length()); } @@ -63,7 +65,7 @@ public static GenericClassDescriptor parseClassSignature(String signature) { public static GenericFieldDescriptor parseFieldSignature(String signature) { try { GenericFieldDescriptor descriptor = new GenericFieldDescriptor(); - descriptor.type = new GenericType(signature); + descriptor.type = GenericType.parse(signature); return descriptor; } catch (RuntimeException e) { @@ -85,19 +87,19 @@ public static GenericMethodDescriptor parseMethodSignature(String signature) { while (pars.length() > 0) { String par = GenericType.getNextType(pars); - descriptor.params.add(new GenericType(par)); + descriptor.params.add(GenericType.parse(par)); pars = pars.substring(par.length()); } String par = GenericType.getNextType(signature); - descriptor.ret = new GenericType(par); + descriptor.ret = GenericType.parse(par); signature = signature.substring(par.length()); if (signature.length() > 0) { String[] exceptions = signature.split("\\^"); for (int i = 1; i < exceptions.length; i++) { - descriptor.exceptions.add(new GenericType(exceptions[i])); + descriptor.exceptions.add(GenericType.parse(exceptions[i])); } } @@ -109,7 +111,7 @@ public static GenericMethodDescriptor parseMethodSignature(String signature) { } } - private static String parseFormalParameters(String signature, List parameters, List> bounds) { + private static String parseFormalParameters(String signature, List parameters, List> bounds) { if (signature.charAt(0) != '<') { return signature; } @@ -142,7 +144,7 @@ private static String parseFormalParameters(String signature, List param String param = value.substring(0, to); value = value.substring(to + 1); - List lstBounds = new ArrayList(); + List lstBounds = new ArrayList(); while (true) { if (value.charAt(0) == ':') { @@ -151,7 +153,7 @@ private static String parseFormalParameters(String signature, List param } String bound = GenericType.getNextType(value); - lstBounds.add(new GenericType(bound)); + lstBounds.add(GenericType.parse(bound)); value = value.substring(bound.length()); @@ -191,69 +193,9 @@ else if (tp == CodeConstants.TYPE_GENVAR) { return type.value; } else if (tp == CodeConstants.TYPE_OBJECT) { - StringBuilder buffer = new StringBuilder(); - appendClassName(type, buffer); - return buffer.toString(); + return type.getCastName(); } throw new RuntimeException("Invalid type: " + type); } - - private static void appendClassName(GenericType type, StringBuilder buffer) { - List enclosingClasses = type.getEnclosingClasses(); - - if (enclosingClasses.isEmpty()) { - String name = type.value.replace('/', '.'); - buffer.append(DecompilerContext.getImportCollector().getShortName(name)); - } - else { - for (GenericType tp : enclosingClasses) { - if (buffer.length() == 0) { - buffer.append(DecompilerContext.getImportCollector().getShortName(tp.value)); - } - else { - buffer.append(tp.value); - } - - appendTypeArguments(tp, buffer); - buffer.append('.'); - } - - buffer.append(type.value); - } - - appendTypeArguments(type, buffer); - } - - private static void appendTypeArguments(GenericType type, StringBuilder buffer) { - if (!type.getArguments().isEmpty()) { - buffer.append('<'); - - for (int i = 0; i < type.getArguments().size(); i++) { - if (i > 0) { - buffer.append(", "); - } - - int wildcard = type.getWildcards().get(i); - switch (wildcard) { - case GenericType.WILDCARD_UNBOUND: - buffer.append('?'); - break; - case GenericType.WILDCARD_EXTENDS: - buffer.append("? extends "); - break; - case GenericType.WILDCARD_SUPER: - buffer.append("? super "); - break; - } - - GenericType genPar = type.getArguments().get(i); - if (genPar != null) { - buffer.append(getGenericCastTypeName(genPar)); - } - } - - buffer.append(">"); - } - } } diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMethodDescriptor.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMethodDescriptor.java index 7ef9399..5c2ff05 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMethodDescriptor.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMethodDescriptor.java @@ -18,15 +18,17 @@ import java.util.ArrayList; import java.util.List; +import org.jetbrains.java.decompiler.struct.gen.VarType; + public class GenericMethodDescriptor { public final List fparameters = new ArrayList(); - public final List> fbounds = new ArrayList>(); + public final List> fbounds = new ArrayList>(); - public final List params = new ArrayList(); + public final List params = new ArrayList(); - public GenericType ret; + public VarType ret; - public final List exceptions = new ArrayList(); + public final List exceptions = new ArrayList(); } diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java index 9305bf9..c45d47f 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java @@ -16,38 +16,43 @@ package org.jetbrains.java.decompiler.struct.gen.generics; import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.struct.gen.VarType; import java.util.ArrayList; +import java.util.Collections; import java.util.List; -public class GenericType { +public class GenericType extends VarType { public static final int WILDCARD_EXTENDS = 1; public static final int WILDCARD_SUPER = 2; public static final int WILDCARD_UNBOUND = 3; public static final int WILDCARD_NO = 4; - public final int type; - public final int arrayDim; - public final String value; - public final int stackSize; + private final VarType parent; + private final List arguments; + private final int wildcard; - private final List enclosingClasses = new ArrayList(); - private final List arguments = new ArrayList(); - private final List wildcards = new ArrayList(); + private GenericType(int type, int arrayDim, String value, VarType parent, List arguments, int wildcard) { + super(type, arrayDim, value, getFamily(type, arrayDim), getStackSize(type, arrayDim), false); + this.parent = parent; + this.arguments = arguments == null ? Collections.emptyList() : arguments; + this.wildcard = wildcard; + } - public GenericType(int type, int arrayDim, String value) { - this.type = type; - this.arrayDim = arrayDim; - this.value = value; - this.stackSize = getStackSize(type, arrayDim); + public static VarType parse(String signature) { + return parse(signature, WILDCARD_NO); } - public GenericType(String signature) { + public static VarType parse(String signature, int wildcard) { int type = 0; int arrayDim = 0; String value = null; + List params = null; + VarType parent = null; + int index = 0; loop: @@ -65,32 +70,53 @@ public GenericType(String signature) { case 'L': type = CodeConstants.TYPE_OBJECT; signature = signature.substring(index + 1, signature.length() - 1); + String cl = getNextClassSignature(signature); - while (true) { - String cl = getNextClassSignature(signature); - - String name = cl; - String args = null; - - int argStart = cl.indexOf("<"); + if (cl.length() == signature.length()) { + int argStart = cl.indexOf('<'); if (argStart >= 0) { - name = cl.substring(0, argStart); - args = cl.substring(argStart + 1, cl.length() - 1); - } - - if (cl.length() < signature.length()) { - signature = signature.substring(cl.length() + 1); // skip '.' - GenericType type11 = new GenericType(CodeConstants.TYPE_OBJECT, 0, name); - parseArgumentsList(args, type11); - enclosingClasses.add(type11); + value = cl.substring(0, argStart); + params = parseArgumentsList(cl.substring(argStart + 1, cl.length() - 1)); } else { - value = name; - parseArgumentsList(args, this); - break; + value = cl; + } + } + else { + StringBuilder name_buff = new StringBuilder(); + while (signature.length() > 0) { + String name = cl; + String args = null; + + int argStart = cl.indexOf('<'); + if (argStart >= 0) { + name = cl.substring(0, argStart); + args = cl.substring(argStart + 1, cl.length() - 1); + } + + if (name_buff.length() > 0) { + name_buff.append('.'); + } + name_buff.append(name); + + value = name_buff.toString(); + params = args == null ? null : parseArgumentsList(args); + + if (cl.length() == signature.length()) { + break; + } + else { + if (parent == null && params == null) { + parent = GenericType.parse("L" + value + ";"); + } + else { + parent = new GenericType(CodeConstants.TYPE_OBJECT, 0, value, parent, params, wildcard); + } + + signature = signature.substring(cl.length() + 1); + } } } - break loop; default: @@ -101,10 +127,20 @@ public GenericType(String signature) { index++; } - this.type = type; - this.arrayDim = arrayDim; - this.value = value; - this.stackSize = getStackSize(type, arrayDim); + if (type == CodeConstants.TYPE_GENVAR) { + return new GenericType(type, arrayDim, value, null, null, wildcard); + } + else if (type == CodeConstants.TYPE_OBJECT) { + if (parent == null && params == null && wildcard == WILDCARD_NO) { + return new VarType(type, arrayDim, value); + } + else { + return new GenericType(type, arrayDim, value, parent, params, wildcard); + } + } + else { + return new VarType(type, arrayDim, value); + } } private static String getNextClassSignature(String value) { @@ -132,11 +168,13 @@ private static String getNextClassSignature(String value) { return value.substring(0, index); } - private static void parseArgumentsList(String value, GenericType type) { + private static List parseArgumentsList(String value) { if (value == null) { - return; + return null; } + List args = new ArrayList(); + while (value.length() > 0) { String typeStr = getNextType(value); int len = typeStr.length(); @@ -154,16 +192,16 @@ private static void parseArgumentsList(String value, GenericType type) { break; } - type.getWildcards().add(wildcard); - if (wildcard != WILDCARD_NO) { typeStr = typeStr.substring(1); } - type.getArguments().add(typeStr.length() == 0 ? null : new GenericType(typeStr)); + args.add(typeStr.length() == 0 ? null : GenericType.parse(typeStr, wildcard)); value = value.substring(len); } + + return args; } public static String getNextType(String value) { @@ -212,37 +250,71 @@ public static String getNextType(String value) { return value.substring(0, index + 1); } - private static int getStackSize(int type, int arrayDim) { - if (arrayDim > 0) { - return 1; - } - - switch (type) { - case CodeConstants.TYPE_DOUBLE: - case CodeConstants.TYPE_LONG: - return 2; - case CodeConstants.TYPE_VOID: - case CodeConstants.TYPE_GROUP2EMPTY: - return 0; - default: - return 1; - } - } - public GenericType decreaseArrayDim() { assert arrayDim > 0 : this; - return new GenericType(type, arrayDim - 1, value); + return new GenericType(type, arrayDim - 1, value, parent, arguments, wildcard); } - public List getArguments() { + public List getArguments() { return arguments; } - public List getEnclosingClasses() { - return enclosingClasses; + @Override + public boolean isGeneric() { + return true; + } + + public int getWildcard() { + return wildcard; + } + + public String getCastName() { + String clsName = null; + if (parent == null) { + clsName = DecompilerContext.getImportCollector().getShortName(value.replace('/', '.')); + } + else if (parent.isGeneric()) { + clsName = ((GenericType)parent).getCastName() + "." + value.substring(value.lastIndexOf('.') + 1); + } + else { + clsName = DecompilerContext.getImportCollector().getShortName(parent.value.replace('/', '.')) + "." + value.substring(value.lastIndexOf('.') + 1); + } + return clsName + getTypeArguments(); } - public List getWildcards() { - return wildcards; + private String getTypeArguments() { + StringBuilder buffer = new StringBuilder(); + if (!arguments.isEmpty()) { + buffer.append('<'); + + for (int i = 0; i < arguments.size(); i++) { + if (i > 0) { + buffer.append(", "); + } + + VarType par = arguments.get(i); + if (par == null) { // Wildcard unbound + buffer.append('?'); + } + else if (par.isGeneric()) { + GenericType gen = (GenericType)par; + switch (gen.getWildcard()) { + case GenericType.WILDCARD_EXTENDS: + buffer.append("? extends "); + break; + case GenericType.WILDCARD_SUPER: + buffer.append("? super "); + break; + } + buffer.append(GenericMain.getGenericCastTypeName(gen)); + } + else { + buffer.append(ExprProcessor.getCastTypeName(par)); + } + } + + buffer.append(">"); + } + return buffer.toString(); } } From 0fac2512ae858770015e5bc2f3d84448ac5031c5 Mon Sep 17 00:00:00 2001 From: cpw Date: Sun, 11 Oct 2015 12:51:22 -0400 Subject: [PATCH 56/73] Fix up the scouting for an init exprent, not to look inside loops for it. Fixes the broken loops. Added tests for it. --- .../main/rels/MethodProcessorRunnable.java | 24 ++++++++- .../modules/decompiler/MergeHelper.java | 2 + .../jetbrains/java/decompiler/LVTTest.java | 4 +- .../java/decompiler/LoopMergingTests.java | 30 +++++++++++ testData/classes/pkg/TestLoopMerging.class | Bin 0 -> 1524 bytes testData/results/TestLoopMerging.dec | 49 ++++++++++++++++++ testData/src/pkg/TestLoopMerging.java | 47 +++++++++++++++++ 7 files changed, 153 insertions(+), 3 deletions(-) create mode 100644 test/org/jetbrains/java/decompiler/LoopMergingTests.java create mode 100644 testData/classes/pkg/TestLoopMerging.class create mode 100644 testData/results/TestLoopMerging.dec create mode 100644 testData/src/pkg/TestLoopMerging.java diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java index 62d5297..2e1668d 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java @@ -28,9 +28,13 @@ import org.jetbrains.java.decompiler.modules.decompiler.deobfuscator.ExceptionDeobfuscator; import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.IfExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.DummyExitStatement; +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.Statement; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; @@ -42,6 +46,10 @@ public class MethodProcessorRunnable implements Runnable { + private static RootStatement currentRoot; + + private static VarProcessor vp; + public final Object lock = new Object(); private final StructMethod method; @@ -126,7 +134,8 @@ public static RootStatement codeToJava(StructMethod mt, VarProcessor varProc) th } RootStatement root = DomHelper.parseGraph(graph, mt); - + MethodProcessorRunnable.currentRoot = root; + MethodProcessorRunnable.vp = varProc; FinallyProcessor fProc = new FinallyProcessor(varProc); while (fProc.iterateGraph(mt, root, graph)) { root = DomHelper.parseGraph(graph, mt); @@ -232,6 +241,9 @@ public boolean isFinished() { return finished; } + public static void printMethod(String desc) { + printMethod(currentRoot, desc, vp); + } public static void printMethod(Statement root, String name, VarProcessor varProc) { System.out.println(name + " {"); if (root == null || root.getSequentialObjects() == null) { @@ -280,7 +292,13 @@ private static void printStatement(Statement statement, String indent, VarProces int start = values.nextSetBit(0); int end = values.length()-1; - System.out.println(indent + "{" + statement.type + "}:" + statement.id + " (" + start + ", " + end + ") " + statement.getClass().getSimpleName()); + System.out.print(indent + "{" + statement.type + "}:" + statement.id + " (" + start + ", " + end + ") " + statement.getClass().getSimpleName()); + if (statement.type == Statement.TYPE_DO) { + System.out.print(" t:"+((DoStatement)statement).getLooptype()); + } else if (statement.type == Statement.TYPE_BASICBLOCK) { + System.out.print(" i:"+((BasicBlockStatement)statement).getBlock().toStringOldIndices().replaceAll("\n", ";")); + } + System.out.println(); for (StatEdge edge : statement.getAllSuccessorEdges()) System.out.println(indent + " Dest: " + edge.getDestination()); @@ -323,6 +341,8 @@ private static String printExprent(String indent, Exprent exp, VarProcessor varP sb.append("{").append(printExprent(" ",assignmentExprent.getLeft(),varProc)).append(" =").append(printExprent(" ",assignmentExprent.getRight(),varProc)).append("}"); } else if (exp instanceof IfExprent) { sb.append(" ").append(exp.toJava(0, new BytecodeMappingTracer())); + } else if (exp instanceof FunctionExprent) { + sb.append(" ").append(exp.toJava(0, new BytecodeMappingTracer())); } return sb.toString(); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java index 11c7c59..c240daa 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java @@ -358,6 +358,8 @@ private static boolean matchFor(DoStatement stat) { } else { preData = current.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD).get(0); + // we're not a basic block, so we can't dive inside for exprents + if (preData.type != Statement.TYPE_BASICBLOCK) break; preData = getLastDirectData(preData); if (preData != null && !preData.getExprents().isEmpty()) { initDoExprent = preData.getExprents().get(preData.getExprents().size() - 1); diff --git a/test/org/jetbrains/java/decompiler/LVTTest.java b/test/org/jetbrains/java/decompiler/LVTTest.java index d4771bb..fea37bc 100644 --- a/test/org/jetbrains/java/decompiler/LVTTest.java +++ b/test/org/jetbrains/java/decompiler/LVTTest.java @@ -38,11 +38,12 @@ public void setUp() throws IOException { // @Test public void testMatch1() { doTest("pkg/TestPPMM"); } // @Test public void testMatchLM() { doTest("pkg/TestLexManosLVT"); } // @Test public void testMatch1() { doTest("pkg/TestLVT"); } +// @Test public void testMatch1() { doTest("pkg/TestLoopMerging"); } // @Test public void testMatch2() { doTest("pkg/TestLVTScoping"); } // @Test public void testMCWorld() { doTest("net/minecraft/world/World"); } // @Test public void testMCGuiCmdBlock() { doTest("net/minecraft/client/gui/GuiCommandBlock"); } - @Test public void testMCWorld() { doTest("net/minecraft/world/World"); } +// @Test public void testMCWorld() { doTest("net/minecraft/world/World"); } // @Test public void testMCGuiCmdBlock() { doTest("net/minecraft/client/gui/GuiCommandBlock"); } @@ -51,4 +52,5 @@ public void setUp() throws IOException { // @Test public void testMCAbstractTexture() { doTest("net/minecraft/client/multiplayer/ServerAddress"); } // @Test public void testMCAbstractResourcePack() { doTest("net/minecraft/client/resources/AbstractResourcePack"); } // @Test public void testMCGuiShareToLan() { doTest("net/minecraft/client/gui/GuiShareToLan"); } +// @Test public void testMCContainerPlayer() { doTest("net/minecraft/inventory/ContainerPlayer"); } } diff --git a/test/org/jetbrains/java/decompiler/LoopMergingTests.java b/test/org/jetbrains/java/decompiler/LoopMergingTests.java new file mode 100644 index 0000000..ddc15d1 --- /dev/null +++ b/test/org/jetbrains/java/decompiler/LoopMergingTests.java @@ -0,0 +1,30 @@ +package org.jetbrains.java.decompiler; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; +import org.junit.Test; + +public class LoopMergingTests extends SingleClassesTestBase { + @SuppressWarnings("serial") + @Override + protected Map getDecompilerOptions() { + return new HashMap() {{ + put(IFernflowerPreferences.USE_DEBUG_LINE_NUMBERS, "1"); + }}; + } + + @Override + public void setUp() throws IOException { + super.setUp(); + fixture.cleanup = false; + } + + @Test + public void testLoopMerging() { + doTest("pkg/TestLoopMerging"); + } + +} diff --git a/testData/classes/pkg/TestLoopMerging.class b/testData/classes/pkg/TestLoopMerging.class new file mode 100644 index 0000000000000000000000000000000000000000..453837353833482c6abb1718c2900e2149662c1f GIT binary patch literal 1524 zcmbu7OHUI~7>3`|nRZGUZY?brLGHJrR1u8;BaY48BAQbz!3g{ zZoI`6CMG5l&_oF#ap(VV!PXyOea|pZi!3l?a?Y7^``-6F-}mG9*IfYJ=+@v72rbRe z#>UNMJDyH2jhLBPD>bV@6)2rE9viU*BQ+blo0v0`wtz6$hS(AUz8hA`vik*;_Kpbw zbuc|`2B1LK;YWc$N!&`AcUBe?W@g+-ESLhpcsgk;Oc)tU&fP`Teq=2RgySdJ=wn!$ z&K!-Q0tl4r(BM-Cw*Wb<(Bb9udk?73PM~PaHj?ur#*!P}EAMv~pcZu+&gVU9?8&li zF6yX90|nA6lnKY3H(Tl0y^NKz$Lx%0EcOM^gcc3WWOc%}j#jh@XiM_OLW-8#hdL(I zTlLb8BY#D}WAKfhsbQj2CH21jf=UnWQ;nNl4|#Qqd(P(ZE;=lje~z;o~0_ zozcrb@@604YK)`c=#73s;V0)p5qqDrsGykt`oRH|pp-L#G6dPn%TFmGFAr>>I6Aq7 zP%u2RjZkz85w_4wu=2&r?0#-Dk+)g({8e^ep8d!W3Z!hY<5?qoKYHmgCqLlISM!t) zsuCy*Q2N}FmA9^y(P-f|A{(gKM2(F78I96eM5nei6$!S})6Y|$$}@MK03{7Stp+)^ z(oz?hT;~|)O3v(PzFB{$EJU*%hh{~DW@Sp&m7PGmbF$aFdrejP$*Ia)+jVozBi0mA z8&=1Tx9XdbhX2uB_^K+p%IvHdIYir>2cS= 180.0F) { + this.b += 360.0F; } + return this.a; } + + public float test2() { + for(this.a = 0.0F; this.a < 10.0F; ++this.a) { + System.out.println(this.a); } + + for(this.a = 0.0F; this.a < 10.0F; ++this.a) { + System.out.println(this.a); } + + return this.a; + } + + public float test3() { + int[] as = new int[0]; + for(int f : as) { + ++f; } + + while(this.a - this.b < -180.0F) { this.b -= 360.0F; } + while(this.a - this.b >= 180.0F) { this.b += 360.0F; + } + for(this.a = 0.0F; this.a < 10.0F; ++this.a) { + System.out.println(this.a); + } + return this.a; } + public float test4() { + int[] as = new int[0]; + for(int f : as) { + ++f; while(this.a - this.b < -180.0F) { + this.b -= 360.0F; } while(this.a - this.b >= 180.0F) { + this.b += 360.0F; } + for(this.a = 0.0F; this.a < 10.0F; ++this.a) { + System.out.println(this.a); while(this.a - this.b < -180.0F) { + this.b -= 360.0F; } while(this.a - this.b >= 180.0F) { + this.b += 360.0F; + } + } + } + + return this.a; + } +} diff --git a/testData/src/pkg/TestLoopMerging.java b/testData/src/pkg/TestLoopMerging.java new file mode 100644 index 0000000..c257b10 --- /dev/null +++ b/testData/src/pkg/TestLoopMerging.java @@ -0,0 +1,47 @@ +package pkg; + +public class TestLoopMerging { + public float a; + public float b; + public float test() { + while(a - b < -180) b -= 360; + while(a - b >= 180) b += 360; + return a; + } + public float test2() { + for (a = 0; a < 10; a++) { + System.out.println(a); + } + for (a = 0; a < 10; a++) { + System.out.println(a); + } + return a; + } + + public float test3() { + int[] as = new int[0]; + for (int f: as) { + f++; + } + while(this.a - this.b < -180) this.b -= 360; + while(this.a - this.b >= 180) this.b += 360; + for (a = 0; a < 10; a++) { + System.out.println(a); + } + return this.a; + } + public float test4() { + int[] as = new int[0]; + for (int f: as) { + f++; + while(this.a - this.b < -180) this.b -= 360; + while(this.a - this.b >= 180) this.b += 360; + for (a = 0; a < 10; a++) { + System.out.println(a); + while(this.a - this.b < -180) this.b -= 360; + while(this.a - this.b >= 180) this.b += 360; + } + } + return this.a; + } +} From cbc3d52186da0f3de950b1fcd1628a31ef46c3d7 Mon Sep 17 00:00:00 2001 From: cpw Date: Mon, 12 Oct 2015 14:44:39 -0400 Subject: [PATCH 57/73] Fix IncludeEntireClasspath so it doesn't dump out random other files from the classpath. Also, it doesn't try and save undecompiled additional classes too. --- src/org/jetbrains/java/decompiler/main/Fernflower.java | 3 ++- src/org/jetbrains/java/decompiler/struct/ContextUnit.java | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/org/jetbrains/java/decompiler/main/Fernflower.java b/src/org/jetbrains/java/decompiler/main/Fernflower.java index 65733ac..6afe975 100644 --- a/src/org/jetbrains/java/decompiler/main/Fernflower.java +++ b/src/org/jetbrains/java/decompiler/main/Fernflower.java @@ -113,7 +113,8 @@ private void addAllClasspath() { if (found.contains(file.getAbsolutePath())) continue; - if (file.exists()) { + // only add .class files from classpath + if (file.exists() && (file.getName().endsWith(".class") || file.getName().endsWith(".jar"))) { DecompilerContext.getLogger().writeMessage("Adding File to context from classpath: " + file, Severity.INFO); structContext.addSpace(file, false); found.add(file.getAbsolutePath()); diff --git a/src/org/jetbrains/java/decompiler/struct/ContextUnit.java b/src/org/jetbrains/java/decompiler/struct/ContextUnit.java index 850c3d8..e304396 100644 --- a/src/org/jetbrains/java/decompiler/struct/ContextUnit.java +++ b/src/org/jetbrains/java/decompiler/struct/ContextUnit.java @@ -110,6 +110,7 @@ public void save() { // classes for (int i = 0; i < classes.size(); i++) { StructClass cl = classes.get(i); + if (!cl.isOwn()) continue; String entryName = decompiledData.getClassEntryName(cl, classEntries.get(i)); if (entryName != null) { String content = decompiledData.getClassContent(cl); From 6b4dc8d90d43ad16cc14df1cdff86f18dd8d0f4f Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Wed, 14 Oct 2015 13:20:13 -0700 Subject: [PATCH 58/73] Fixed generic type parsing of inner classes with generic outers. --- .../java/decompiler/struct/gen/generics/GenericType.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java index c45d47f..aeb786a 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java @@ -95,7 +95,7 @@ public static VarType parse(String signature, int wildcard) { } if (name_buff.length() > 0) { - name_buff.append('.'); + name_buff.append('$'); } name_buff.append(name); @@ -115,6 +115,7 @@ public static VarType parse(String signature, int wildcard) { signature = signature.substring(cl.length() + 1); } + cl = getNextClassSignature(signature); } } break loop; From 6bced8fc859f3f9486d5554604387b4c5a5d34e5 Mon Sep 17 00:00:00 2001 From: RainWarrior Date: Mon, 12 Oct 2015 09:21:36 +0300 Subject: [PATCH 59/73] Foo.baz() generation --- .../modules/decompiler/ExprProcessor.java | 2 +- .../modules/decompiler/exps/Exprent.java | 8 ++ .../modules/decompiler/exps/FieldExprent.java | 15 ++ .../decompiler/exps/InvocationExprent.java | 136 ++++++++++++++++-- .../modules/decompiler/exps/VarExprent.java | 15 ++ .../java/decompiler/struct/gen/VarType.java | 9 ++ 6 files changed, 173 insertions(+), 12 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java index 162d9cb..2760f20 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java @@ -914,7 +914,7 @@ public static boolean getCastedExprent(Exprent exprent, VarType rightType = exprent.getExprType(); - TextBuffer res = exprent.toJava(indent, tracer); + TextBuffer res = exprent.toJava(indent, tracer, leftType); boolean cast = castAlways || diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java index ca9c1ed..9d41ec2 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java @@ -74,6 +74,10 @@ public VarType getExprType() { return VarType.VARTYPE_VOID; } + public VarType getGenericExprType() { + return getExprType(); + } + public int getExprentUse() { return 0; } @@ -126,6 +130,10 @@ public Exprent copy() { throw new RuntimeException("not implemented"); } + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer, VarType expectedType) { + return toJava(indent, tracer); + } + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { throw new RuntimeException("not implemented"); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java index fdc9cec..d4bd68e 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java @@ -23,6 +23,8 @@ import org.jetbrains.java.decompiler.main.rels.MethodWrapper; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; +import org.jetbrains.java.decompiler.struct.StructClass; +import org.jetbrains.java.decompiler.struct.StructField; import org.jetbrains.java.decompiler.struct.consts.LinkConstant; import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; @@ -65,6 +67,19 @@ public VarType getExprType() { return descriptor.type; } + @Override + public VarType getGenericExprType() { + StructClass cl = DecompilerContext.getStructContext().getClass(classname); + while(cl != null) { + StructField ft = cl.getField(name, descriptor.descriptorString); + if(ft != null && ft.getSignature() != null) { + return ft.getSignature().type; + } + cl = DecompilerContext.getStructContext().getClass((String)cl.superClass.value); + } + return getExprType(); + } + @Override public int getExprentUse() { return instance == null ? Exprent.MULTIPLE_USES : instance.getExprentUse() & Exprent.MULTIPLE_USES; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java index 9d16de2..81b6f4e 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java @@ -19,7 +19,9 @@ import java.util.BitSet; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Map.Entry; +import java.util.HashMap; import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; @@ -37,6 +39,7 @@ import org.jetbrains.java.decompiler.struct.consts.LinkConstant; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.struct.gen.generics.GenericType; import org.jetbrains.java.decompiler.struct.match.MatchEngine; import org.jetbrains.java.decompiler.struct.match.MatchNode; import org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue; @@ -194,6 +197,11 @@ public Exprent copy() { @Override public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { + return toJava(indent, tracer, null); + } + + @Override + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer, VarType expectedType) { TextBuffer buf = new TextBuffer(); String super_qualifier = null; @@ -268,6 +276,11 @@ else if (instance.getPrecedence() > getPrecedence()) { } } + List matches = getMatchedDescriptors(); + BitSet setAmbiguousParameters = getAmbiguousParameters(matches); + StructMethod desc = null; + if(matches.size() == 1) desc = matches.get(0); + switch (functype) { case TYP_GENERAL: if (VarExprent.VAR_NAMELESS_ENCLOSURE.equals(buf.toString())) { @@ -276,8 +289,66 @@ else if (instance.getPrecedence() > getPrecedence()) { if (buf.length() > 0) { buf.append("."); - } + boolean add = false; + List toAdd = new ArrayList(); + if(expectedType != null && expectedType.type == CodeConstants.TYPE_OBJECT && descriptor.ret.type == CodeConstants.TYPE_OBJECT) { + //System.out.println("0: " + isStatic + " " + desc + " " + expectedType + " " + descriptor.ret + " " + expectedType.isGeneric()); + if(desc == null) { + // more harn than gain + // Object -> String + /*if(descriptor.ret.value.equals("java/lang/Object") && !descriptor.ret.equals(expectedType)) { + add = true; + toAdd.add(expectedType); + System.out.println("1: " + expectedType + " " + descriptor.ret); + }*/ + // List -> List + if(expectedType.isGeneric()) { + List leftArgs = ((GenericType)expectedType).getArguments(); + //System.out.println("22: " + expectedType + " " + leftArgs.get(0)); + if(leftArgs.size() == 1 && descriptor.ret.equals(expectedType) && leftArgs.get(0).type == CodeConstants.TYPE_OBJECT) { + add = true; + toAdd.add(leftArgs.get(0)); + System.out.println("2: " + expectedType.type + " " + expectedType + " " + leftArgs.get(0).type + " " + leftArgs.get(0)); + } + } + } + if(desc != null && desc.getSignature() != null) { + VarType ret = desc.getSignature().ret; + // more harm than gain + // T -> String + /*if(desc.getSignature().fparameters.size() == 1 && desc.getSignature().fparameters.get(0).equals(ret.value)) { + add = true; + toAdd.add(expectedType); + System.out.println("3: " + expectedType + " " + ret + " " + desc.getSignature().fparameters.get(0)); + } + // List -> List + if(expectedType.isGeneric() && ret.isGeneric()) { + List leftArgs = ((GenericType)expectedType).getArguments(); + List rightArgs = ((GenericType)ret).getArguments(); + List fparams = desc.getSignature().fparameters; + // trivial case only for now + if(leftArgs.size() == 1 && rightArgs.size() == 1 && fparams.size() == 1 && + !leftArgs.get(0).equals(rightArgs.get(0)) && rightArgs.get(0).value.equals(fparams.get(0))) { + add = true; + toAdd.add(leftArgs.get(0)); + System.out.println("4: " + leftArgs.get(0) + " " + rightArgs.get(0) + " " + fparams.get(0)); + } + }*/ + } + if(add && toAdd.size() != 0) { + buf.append("<"); + for(int i = 0; i < toAdd.size(); i++) { + buf.append(ExprProcessor.getCastTypeName(toAdd.get(i))); + if(i + 1 < toAdd.size()) { + buf.append(", "); + } + } + buf.append(">"); + } + } + + } buf.append(name); if (invocationTyp == INVOKE_DYNAMIC) { buf.append(""); @@ -318,8 +389,17 @@ else if (isInstanceThis) { } } - BitSet setAmbiguousParameters = getAmbiguousParameters(); - + /*StructClass cl = DecompilerContext.getStructContext().getClass(classname); + Map genArgs = new HashMap(); + if(cl != null && cl.getSignature() != null && instance != null && instance.getGenericExprType().isGeneric()) { + GenericType genType = (GenericType)instance.getGenericExprType(); + for(int i = 0; i < cl.getSignature().fparameters.size(); i++) { + VarType from = GenericType.parse("T" + cl.getSignature().fparameters.get(i) + ";"); + VarType to = genType.getArguments().get(i); + //System.out.println("(" + from.type + " " + from.value + " -> " + to.type + " " + to.value + ")"); + genArgs.put(from, to); + } + }*/ boolean firstParameter = true; int start = isEnum ? 2 : 0; for (int i = start; i < lstParameters.size(); i++) { @@ -330,7 +410,33 @@ else if (isInstanceThis) { TextBuffer buff = new TextBuffer(); boolean ambiguous = setAmbiguousParameters.get(i); - ExprProcessor.getCastedExprent(lstParameters.get(i), descriptor.params[i], buff, indent, true, ambiguous, tracer); + VarType type = descriptor.params[i]; + if(desc != null && desc.getSignature() != null && desc.getSignature().params.size() == lstParameters.size()) { + VarType newType = desc.getSignature().params.get(i); + boolean free = false; + for(String param : desc.getSignature().fparameters) { + if(param.equals(newType.value)) { + free = true; + break; + } + } + if(!free && desc.getSignature().params.get(i).type == CodeConstants.TYPE_OBJECT) { + type = desc.getSignature().params.get(i); + } + } + /*if(genArgs.containsKey(type)) { + type = genArgs.get(type); + } + if(desc != null && desc.getSignature() != null) { + for(String ps: desc.getSignature().fparameters) { + VarType param = GenericType.parse("T" + ps + ";"); + if(param.equals(type)) { // found free argument, need to infer it from the argument + type = lstParameters.get(i).getExprType(); + break; + } + } + }*/ + ExprProcessor.getCastedExprent(lstParameters.get(i), type, buff, indent, true, ambiguous, tracer); buf.append(buff); firstParameter = false; @@ -342,12 +448,12 @@ else if (isInstanceThis) { return buf; } - private BitSet getAmbiguousParameters() { + private List getMatchedDescriptors() { + List matches = new ArrayList(); + StructClass cl = DecompilerContext.getStructContext().getClass(classname); - if (cl == null) return EMPTY_BIT_SET; + if (cl == null) return matches; - // check number of matches - List matches = new ArrayList(); nextMethod: for (StructMethod mt : cl.getMethods()) { if (name.equals(mt.getName())) { @@ -358,10 +464,17 @@ private BitSet getAmbiguousParameters() { continue nextMethod; } } - matches.add(md); - } + matches.add(mt); + } } } + return matches; + } + + private BitSet getAmbiguousParameters(List matches) { + StructClass cl = DecompilerContext.getStructContext().getClass(classname); + if (cl == null) return EMPTY_BIT_SET; + if (matches.size() == 1) return EMPTY_BIT_SET; // check if a call is unambiguous @@ -384,7 +497,8 @@ private BitSet getAmbiguousParameters() { BitSet ambiguous = new BitSet(descriptor.params.length); for (int i = 0; i < descriptor.params.length; i++) { VarType paramType = descriptor.params[i]; - for (MethodDescriptor md : matches) { + for (StructMethod mtt : matches) { + MethodDescriptor md = MethodDescriptor.parseDescriptor(mtt.getDescriptor()); if (!paramType.equals(md.params[i])) { ambiguous.set(i); break; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java index 4784e03..3058e77 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java @@ -65,6 +65,21 @@ public VarType getExprType() { return getVarType(); } + @Override + public VarType getGenericExprType() { + if (lvt != null && lvt.getSig() != null) { + // TODO; figure out why it's crashing, ugly fix for now + try { + return GenericType.parse(lvt.getSig()); + } catch (StringIndexOutOfBoundsException ex) { + } + } + else if (lvt != null) { + return lvt.getVarType(); + } + return getVarType(); + } + @Override public int getExprentUse() { return Exprent.MULTIPLE_USES | Exprent.SIDE_EFFECTS_FREE; diff --git a/src/org/jetbrains/java/decompiler/struct/gen/VarType.java b/src/org/jetbrains/java/decompiler/struct/gen/VarType.java index 1b0df9d..af06b8e 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/VarType.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/VarType.java @@ -269,6 +269,15 @@ else if (this.equals(VARTYPE_OBJECT)) { return res; } + @Override + public int hashCode() { + int result = 1; + result = 37 * result + type; + result = 37 * result + arrayDim; + result = 37 * result + value == null ? 0 : value.hashCode(); + return result; + } + @Override public boolean equals(Object o) { if (o == this) { From b84e34a3999001b3da2bed7766c1ed2a77bcd100 Mon Sep 17 00:00:00 2001 From: RainWarrior Date: Wed, 14 Oct 2015 05:45:41 +0300 Subject: [PATCH 60/73] concentrated changes to Exprent.getInferredExprType --- .../java/decompiler/main/ClassWriter.java | 1 + .../modules/decompiler/ExprProcessor.java | 4 +- .../decompiler/exps/AssignmentExprent.java | 8 +- .../modules/decompiler/exps/Exprent.java | 6 +- .../modules/decompiler/exps/FieldExprent.java | 5 +- .../decompiler/exps/InvocationExprent.java | 189 ++++++++++-------- .../modules/decompiler/exps/VarExprent.java | 2 +- .../struct/gen/generics/GenericType.java | 16 ++ .../MinecraftDecompilationTest.java | 1 + 9 files changed, 139 insertions(+), 93 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index 4ec7fe9..7c8276c 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -435,6 +435,7 @@ private void fieldToJava(ClassWrapper wrapper, StructClass cl, StructField fd, T else { buffer.append(" = "); // FIXME: special case field initializer. Can map to more than one method (constructor) and bytecode intruction. + initializer.getInferredExprType(descriptor == null? fieldType : descriptor.type); buffer.append(initializer.toJava(indent, tracer)); } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java index 2760f20..3be3b6f 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java @@ -912,9 +912,9 @@ public static boolean getCastedExprent(Exprent exprent, boolean castAlways, BytecodeMappingTracer tracer) { - VarType rightType = exprent.getExprType(); + VarType rightType = exprent.getInferredExprType(leftType); - TextBuffer res = exprent.toJava(indent, tracer, leftType); + TextBuffer res = exprent.toJava(indent, tracer); boolean cast = castAlways || diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssignmentExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssignmentExprent.java index efc64b9..8276abd 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssignmentExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssignmentExprent.java @@ -65,6 +65,12 @@ public VarType getExprType() { return left.getExprType(); } + @Override + public VarType getInferredExprType(VarType upperBound) { + //System.out.println("infer: " + upperBound + " = " + getExprType()); + return getExprType(); + } + @Override public CheckTypesResult checkExprTypeBounds() { CheckTypesResult result = new CheckTypesResult(); @@ -106,7 +112,7 @@ public int getPrecedence() { @Override public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { VarType leftType = left.getExprType(); - VarType rightType = right.getExprType(); + VarType rightType = right.getInferredExprType(leftType); boolean fieldInClassInit = false, hiddenField = false; if (left.type == Exprent.EXPRENT_FIELD) { // first assignment to a final field. Field name without "this" in front of it diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java index 9d41ec2..bda80a0 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java @@ -74,7 +74,7 @@ public VarType getExprType() { return VarType.VARTYPE_VOID; } - public VarType getGenericExprType() { + public VarType getInferredExprType(VarType upperBound) { return getExprType(); } @@ -130,10 +130,6 @@ public Exprent copy() { throw new RuntimeException("not implemented"); } - public TextBuffer toJava(int indent, BytecodeMappingTracer tracer, VarType expectedType) { - return toJava(indent, tracer); - } - public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { throw new RuntimeException("not implemented"); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java index d4bd68e..be95dc1 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java @@ -68,14 +68,15 @@ public VarType getExprType() { } @Override - public VarType getGenericExprType() { + public VarType getInferredExprType(VarType upperBound) { StructClass cl = DecompilerContext.getStructContext().getClass(classname); while(cl != null) { StructField ft = cl.getField(name, descriptor.descriptorString); if(ft != null && ft.getSignature() != null) { return ft.getSignature().type; } - cl = DecompilerContext.getStructContext().getClass((String)cl.superClass.value); + if(cl.superClass == null) cl = null; + else cl = DecompilerContext.getStructContext().getClass((String)cl.superClass.value); } return getExprType(); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java index 81b6f4e..ca5e077 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java @@ -72,6 +72,8 @@ public class InvocationExprent extends Exprent { private int invocationTyp = INVOKE_VIRTUAL; private List lstParameters = new ArrayList(); + private List genericArgs = new ArrayList(); + public InvocationExprent() { super(EXPRENT_INVOCATION); } @@ -163,6 +165,65 @@ public VarType getExprType() { return descriptor.ret; } + @Override + public VarType getInferredExprType(VarType upperBound) { + //System.out.println("infer: " + instance + " " + classname + "." + name + " " + getExprType() + " " + upperBound); + List matches = getMatchedDescriptors(); + StructMethod desc = null; + if(matches.size() == 1) desc = matches.get(0); + + VarType type = getExprType(); + + List toAdd = new ArrayList(); + if(upperBound != null/* && upperBound.type == CodeConstants.TYPE_OBJECT && descriptor.ret.type == CodeConstants.TYPE_OBJECT*/) { + genericArgs.clear(); + //System.out.println("0: " + isStatic + " " + desc + " " + upperBound + " " + descriptor.ret + " " + upperBound.isGeneric()); + /*if(desc == null) { + // more harn than gain + // Object -> String + if(descriptor.ret.value.equals("java/lang/Object") && !descriptor.ret.equals(upperBound)) { + genericArgs.add(upperBound); + System.out.println("1: " + upperBound + " " + descriptor.ret); + } + // List -> List + if(upperBound.isGeneric()) { + List leftArgs = ((GenericType)upperBound).getArguments(); + //System.out.println("22: " + upperBound + " " + leftArgs.get(0)); + if(leftArgs.size() == 1 && descriptor.ret.equals(upperBound) && leftArgs.get(0).type == CodeConstants.TYPE_OBJECT) { + genericArgs.add(leftArgs.get(0)); + System.out.println("2: " + upperBound.type + " " + upperBound + " " + leftArgs.get(0).type + " " + leftArgs.get(0)); + } + } + }*/ + if(desc != null && desc.getSignature() != null) { + VarType ret = desc.getSignature().ret; + // more harm than gain + // T -> String + /*if(desc.getSignature().fparameters.size() == 1 && desc.getSignature().fparameters.get(0).equals(ret.value)) { + genericArgs.add(upperBound); + System.out.println("3: " + upperBound + " " + ret + " " + desc.getSignature().fparameters.get(0)); + }*/ + // List -> List + if(upperBound.isGeneric() && ret.isGeneric()) { + List leftArgs = ((GenericType)upperBound).getArguments(); + List rightArgs = ((GenericType)ret).getArguments(); + List fparams = desc.getSignature().fparameters; + // trivial case only for now + if(leftArgs.size() == 1 && rightArgs.size() == 1 && fparams.size() == 1) { + VarType l = leftArgs.get(0); + VarType r = rightArgs.get(0); + if(l != null /*&& l.type == CodeConstants.TYPE_OBJECT && !l.equals(r)*/ && r.value.equals(fparams.get(0))) { + genericArgs.add(leftArgs.get(0)); + //type = upperBound; + System.out.println("4: " + leftArgs.get(0) + " " + rightArgs.get(0) + " " + fparams.get(0)); + } + } + } + } + } + + return type; + } @Override public CheckTypesResult checkExprTypeBounds() { CheckTypesResult result = new CheckTypesResult(); @@ -197,11 +258,6 @@ public Exprent copy() { @Override public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { - return toJava(indent, tracer, null); - } - - @Override - public TextBuffer toJava(int indent, BytecodeMappingTracer tracer, VarType expectedType) { TextBuffer buf = new TextBuffer(); String super_qualifier = null; @@ -276,11 +332,6 @@ else if (instance.getPrecedence() > getPrecedence()) { } } - List matches = getMatchedDescriptors(); - BitSet setAmbiguousParameters = getAmbiguousParameters(matches); - StructMethod desc = null; - if(matches.size() == 1) desc = matches.get(0); - switch (functype) { case TYP_GENERAL: if (VarExprent.VAR_NAMELESS_ENCLOSURE.equals(buf.toString())) { @@ -290,64 +341,16 @@ else if (instance.getPrecedence() > getPrecedence()) { if (buf.length() > 0) { buf.append("."); - boolean add = false; - List toAdd = new ArrayList(); - if(expectedType != null && expectedType.type == CodeConstants.TYPE_OBJECT && descriptor.ret.type == CodeConstants.TYPE_OBJECT) { - //System.out.println("0: " + isStatic + " " + desc + " " + expectedType + " " + descriptor.ret + " " + expectedType.isGeneric()); - if(desc == null) { - // more harn than gain - // Object -> String - /*if(descriptor.ret.value.equals("java/lang/Object") && !descriptor.ret.equals(expectedType)) { - add = true; - toAdd.add(expectedType); - System.out.println("1: " + expectedType + " " + descriptor.ret); - }*/ - // List -> List - if(expectedType.isGeneric()) { - List leftArgs = ((GenericType)expectedType).getArguments(); - //System.out.println("22: " + expectedType + " " + leftArgs.get(0)); - if(leftArgs.size() == 1 && descriptor.ret.equals(expectedType) && leftArgs.get(0).type == CodeConstants.TYPE_OBJECT) { - add = true; - toAdd.add(leftArgs.get(0)); - System.out.println("2: " + expectedType.type + " " + expectedType + " " + leftArgs.get(0).type + " " + leftArgs.get(0)); - } - } - } - if(desc != null && desc.getSignature() != null) { - VarType ret = desc.getSignature().ret; - // more harm than gain - // T -> String - /*if(desc.getSignature().fparameters.size() == 1 && desc.getSignature().fparameters.get(0).equals(ret.value)) { - add = true; - toAdd.add(expectedType); - System.out.println("3: " + expectedType + " " + ret + " " + desc.getSignature().fparameters.get(0)); + if(genericArgs.size() != 0) { + buf.append("<"); + for(int i = 0; i < genericArgs.size(); i++) { + buf.append(ExprProcessor.getCastTypeName(genericArgs.get(i))); + if(i + 1 < genericArgs.size()) { + buf.append(", "); } - // List -> List - if(expectedType.isGeneric() && ret.isGeneric()) { - List leftArgs = ((GenericType)expectedType).getArguments(); - List rightArgs = ((GenericType)ret).getArguments(); - List fparams = desc.getSignature().fparameters; - // trivial case only for now - if(leftArgs.size() == 1 && rightArgs.size() == 1 && fparams.size() == 1 && - !leftArgs.get(0).equals(rightArgs.get(0)) && rightArgs.get(0).value.equals(fparams.get(0))) { - add = true; - toAdd.add(leftArgs.get(0)); - System.out.println("4: " + leftArgs.get(0) + " " + rightArgs.get(0) + " " + fparams.get(0)); - } - }*/ - } - if(add && toAdd.size() != 0) { - buf.append("<"); - for(int i = 0; i < toAdd.size(); i++) { - buf.append(ExprProcessor.getCastTypeName(toAdd.get(i))); - if(i + 1 < toAdd.size()) { - buf.append(", "); - } - } - buf.append(">"); } + buf.append(">"); } - } buf.append(name); if (invocationTyp == INVOKE_DYNAMIC) { @@ -389,17 +392,31 @@ else if (isInstanceThis) { } } - /*StructClass cl = DecompilerContext.getStructContext().getClass(classname); + List matches = getMatchedDescriptors(); + BitSet setAmbiguousParameters = getAmbiguousParameters(matches); + StructMethod desc = null; + if(matches.size() == 1) desc = matches.get(0); + + StructClass cl = DecompilerContext.getStructContext().getClass(classname); Map genArgs = new HashMap(); - if(cl != null && cl.getSignature() != null && instance != null && instance.getGenericExprType().isGeneric()) { - GenericType genType = (GenericType)instance.getGenericExprType(); - for(int i = 0; i < cl.getSignature().fparameters.size(); i++) { - VarType from = GenericType.parse("T" + cl.getSignature().fparameters.get(i) + ";"); - VarType to = genType.getArguments().get(i); - //System.out.println("(" + from.type + " " + from.value + " -> " + to.type + " " + to.value + ")"); - genArgs.put(from, to); + + if(cl != null && cl.getSignature() != null && instance != null && instance.getInferredExprType(null).isGeneric()) { + GenericType genType = (GenericType)instance.getInferredExprType(null); + if(genType.getArguments().size() == cl.getSignature().fparameters.size()) { + /*System.out.println("remap: " + classname + "." + name); + if(instance instanceof FieldExprent) { + System.out.println(((FieldExprent)instance).getClassname() + "." + ((FieldExprent)instance).getName()); + }*/ + for(int i = 0; i < cl.getSignature().fparameters.size(); i++) { + VarType from = GenericType.parse("T" + cl.getSignature().fparameters.get(i) + ";"); + VarType to = genType.getArguments().get(i); + //System.out.println("(" + from.type + " " + from.value + " -> " + to.type + " " + to.value + ")"); + if(from != null && to != null && to.type == CodeConstants.TYPE_OBJECT) { + genArgs.put(from, to); + } + } } - }*/ + } boolean firstParameter = true; int start = isEnum ? 2 : 0; for (int i = start; i < lstParameters.size(); i++) { @@ -411,8 +428,9 @@ else if (isInstanceThis) { TextBuffer buff = new TextBuffer(); boolean ambiguous = setAmbiguousParameters.get(i); VarType type = descriptor.params[i]; - if(desc != null && desc.getSignature() != null && desc.getSignature().params.size() == lstParameters.size()) { - VarType newType = desc.getSignature().params.get(i); + VarType newType = null; + /*if(desc != null && desc.getSignature() != null && desc.getSignature().params.size() == lstParameters.size()) { + newType = desc.getSignature().params.get(i); boolean free = false; for(String param : desc.getSignature().fparameters) { if(param.equals(newType.value)) { @@ -420,14 +438,14 @@ else if (isInstanceThis) { break; } } - if(!free && desc.getSignature().params.get(i).type == CodeConstants.TYPE_OBJECT) { - type = desc.getSignature().params.get(i); + if(!free) { + type = newType; } - } - /*if(genArgs.containsKey(type)) { + }*/ + if(genArgs.containsKey(type)) { type = genArgs.get(type); } - if(desc != null && desc.getSignature() != null) { + /*if(desc != null && desc.getSignature() != null) { for(String ps: desc.getSignature().fparameters) { VarType param = GenericType.parse("T" + ps + ";"); if(param.equals(type)) { // found free argument, need to infer it from the argument @@ -436,6 +454,11 @@ else if (isInstanceThis) { } } }*/ + VarType exprType = lstParameters.get(i).getInferredExprType(type); + if(exprType != null && exprType.type != CodeConstants.TYPE_NULL && type != null && type.type == CodeConstants.TYPE_GENVAR) { + type = exprType; + } + //System.out.println("param: " + i + " " + newType + " " + exprType + " " + type + " " + lstParameters.get(i)); ExprProcessor.getCastedExprent(lstParameters.get(i), type, buff, indent, true, ambiguous, tracer); buf.append(buff); @@ -452,6 +475,7 @@ private List getMatchedDescriptors() { List matches = new ArrayList(); StructClass cl = DecompilerContext.getStructContext().getClass(classname); + //System.out.println("m: " + classname + "." + name + " " + cl); if (cl == null) return matches; nextMethod: @@ -485,7 +509,8 @@ private BitSet getAmbiguousParameters(List matches) { boolean exact = true; for (int i = 0; i < md.params.length; i++) { if (!md.params[i].equals(lstParameters.get(i).getExprType())) { - exact = false; + // FIXME + //exact = false; break; } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java index 3058e77..0d41725 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java @@ -66,7 +66,7 @@ public VarType getExprType() { } @Override - public VarType getGenericExprType() { + public VarType getInferredExprType(VarType upperBound) { if (lvt != null && lvt.getSig() != null) { // TODO; figure out why it's crashing, ugly fix for now try { diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java index aeb786a..da04e00 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java @@ -318,4 +318,20 @@ else if (par.isGeneric()) { } return buffer.toString(); } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + switch(getWildcard()) { + case GenericType.WILDCARD_EXTENDS: + buf.append("? extends "); + break; + case GenericType.WILDCARD_SUPER: + buf.append("? super "); + break; + } + buf.append(super.toString()); + buf.append(getTypeArguments()); + return buf.toString(); + } } diff --git a/test/org/jetbrains/java/decompiler/MinecraftDecompilationTest.java b/test/org/jetbrains/java/decompiler/MinecraftDecompilationTest.java index c7e781e..2afd2e6 100644 --- a/test/org/jetbrains/java/decompiler/MinecraftDecompilationTest.java +++ b/test/org/jetbrains/java/decompiler/MinecraftDecompilationTest.java @@ -47,6 +47,7 @@ public void setUp() throws IOException { put(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES,"1"); put(IFernflowerPreferences.ASCII_STRING_CHARACTERS,"1"); put(IFernflowerPreferences.REMOVE_SYNTHETIC,"0"); + put(IFernflowerPreferences.INCLUDE_ENTIRE_CLASSPATH, "1"); }}; fixture.setUp(mcFFOptions); if (!new File(fixture.getTestDataDir(), MC_JAR).exists()) { From 155a27086d2f5178ce4e981e208d69ce8e9a421d Mon Sep 17 00:00:00 2001 From: RainWarrior Date: Wed, 14 Oct 2015 09:26:05 +0300 Subject: [PATCH 61/73] passing information from InvocationExprent's inferred type arguments to the parameters --- .../decompiler/exps/InvocationExprent.java | 42 +++++++++++++++++-- .../struct/gen/generics/GenericType.java | 6 ++- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java index ca5e077..884a2d7 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java @@ -429,7 +429,7 @@ else if (isInstanceThis) { boolean ambiguous = setAmbiguousParameters.get(i); VarType type = descriptor.params[i]; VarType newType = null; - /*if(desc != null && desc.getSignature() != null && desc.getSignature().params.size() == lstParameters.size()) { + if(desc != null && desc.getSignature() != null && genericArgs.size() != 0 && desc.getSignature().params.size() == lstParameters.size()) { newType = desc.getSignature().params.get(i); boolean free = false; for(String param : desc.getSignature().fparameters) { @@ -441,10 +441,44 @@ else if (isInstanceThis) { if(!free) { type = newType; } - }*/ + } if(genArgs.containsKey(type)) { type = genArgs.get(type); } + else if(desc != null && desc.getSignature() != null && genericArgs.size() != 0) { + Map genMap = new HashMap(); + for(int j = 0; j < genericArgs.size(); j++) { + VarType from = GenericType.parse("T" + desc.getSignature().fparameters.get(j) + ";"); + VarType to = genericArgs.get(j); + genMap.put(from, to); + //System.out.println("map: (" + from + " -> " + to + ")"); + } + if(genMap.containsKey(type)) { + type = genMap.get(type); + } + // this only checks 1 level deep right now + else if(type.isGeneric()) { + GenericType genType = (GenericType)type; + List toArgs = new ArrayList(); + boolean changed = false; + VarType parent = genType.getParent(); + if(genMap.containsKey(parent)) { + parent = genMap.get(parent); + changed = true; + } + for(VarType arg : genType.getArguments()) { + if(genMap.containsKey(arg)) { + toArgs.add(genMap.get(arg)); + changed = true; + } else { + toArgs.add(arg); + } + } + if(changed) { + type = new GenericType(type.type, type.arrayDim, type.value, parent, toArgs, genType.getWildcard()); + } + } + } /*if(desc != null && desc.getSignature() != null) { for(String ps: desc.getSignature().fparameters) { VarType param = GenericType.parse("T" + ps + ";"); @@ -509,8 +543,7 @@ private BitSet getAmbiguousParameters(List matches) { boolean exact = true; for (int i = 0; i < md.params.length; i++) { if (!md.params[i].equals(lstParameters.get(i).getExprType())) { - // FIXME - //exact = false; + exact = false; break; } } @@ -523,6 +556,7 @@ private BitSet getAmbiguousParameters(List matches) { for (int i = 0; i < descriptor.params.length; i++) { VarType paramType = descriptor.params[i]; for (StructMethod mtt : matches) { + if(mtt.getSignature() != null && mtt.getSignature().params.get(i).isGeneric()) break; MethodDescriptor md = MethodDescriptor.parseDescriptor(mtt.getDescriptor()); if (!paramType.equals(md.params[i])) { ambiguous.set(i); diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java index da04e00..9319bf7 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java @@ -35,7 +35,7 @@ public class GenericType extends VarType { private final List arguments; private final int wildcard; - private GenericType(int type, int arrayDim, String value, VarType parent, List arguments, int wildcard) { + public GenericType(int type, int arrayDim, String value, VarType parent, List arguments, int wildcard) { super(type, arrayDim, value, getFamily(type, arrayDim), getStackSize(type, arrayDim), false); this.parent = parent; this.arguments = arguments == null ? Collections.emptyList() : arguments; @@ -256,6 +256,10 @@ public GenericType decreaseArrayDim() { return new GenericType(type, arrayDim - 1, value, parent, arguments, wildcard); } + public VarType getParent() { + return parent; + } + public List getArguments() { return arguments; } From f8df185c9f5ecc896aac6f264196e1d842796b87 Mon Sep 17 00:00:00 2001 From: RainWarrior Date: Wed, 14 Oct 2015 14:18:10 +0300 Subject: [PATCH 62/73] pushed remapping to the VarType, added type inference to a couple more places --- .../modules/decompiler/ExprProcessor.java | 1 + .../decompiler/exps/AssignmentExprent.java | 2 +- .../decompiler/exps/InvocationExprent.java | 114 ++++++++++-------- .../java/decompiler/struct/gen/VarType.java | 9 ++ .../struct/gen/generics/GenericType.java | 40 +++++- 5 files changed, 108 insertions(+), 58 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java index 3be3b6f..60832d3 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java @@ -853,6 +853,7 @@ public static TextBuffer listToJava(List lst, int indent, BytecodeMappi lst = Exprent.sortIndexed(lst); for (Exprent expr : lst) { + expr.getInferredExprType(null); TextBuffer content = expr.toJava(indent, tracer); if (content.length() > 0) { if (expr.type != Exprent.EXPRENT_VAR || !((VarExprent)expr).isClassDef()) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssignmentExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssignmentExprent.java index 8276abd..68f5830 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssignmentExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssignmentExprent.java @@ -112,7 +112,7 @@ public int getPrecedence() { @Override public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { VarType leftType = left.getExprType(); - VarType rightType = right.getInferredExprType(leftType); + VarType rightType = right.getInferredExprType(left.getInferredExprType(null)); boolean fieldInClassInit = false, hiddenField = false; if (left.type == Exprent.EXPRENT_FIELD) { // first assignment to a final field. Field name without "this" in front of it diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java index 884a2d7..4c4b898 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java @@ -167,10 +167,10 @@ public VarType getExprType() { @Override public VarType getInferredExprType(VarType upperBound) { - //System.out.println("infer: " + instance + " " + classname + "." + name + " " + getExprType() + " " + upperBound); List matches = getMatchedDescriptors(); StructMethod desc = null; if(matches.size() == 1) desc = matches.get(0); + //System.out.println("infer: " + instance + " " + classname + "." + name + " " + getExprType() + " " + upperBound + " " + desc); VarType type = getExprType(); @@ -403,15 +403,15 @@ else if (isInstanceThis) { if(cl != null && cl.getSignature() != null && instance != null && instance.getInferredExprType(null).isGeneric()) { GenericType genType = (GenericType)instance.getInferredExprType(null); if(genType.getArguments().size() == cl.getSignature().fparameters.size()) { - /*System.out.println("remap: " + classname + "." + name); - if(instance instanceof FieldExprent) { - System.out.println(((FieldExprent)instance).getClassname() + "." + ((FieldExprent)instance).getName()); - }*/ + /*System.out.println("remap: " + classname + "." + name + " " + genType); + if(instance instanceof FieldExprent) { + System.out.println(((FieldExprent)instance).getClassname() + "." + ((FieldExprent)instance).getName()); + }*/ for(int i = 0; i < cl.getSignature().fparameters.size(); i++) { VarType from = GenericType.parse("T" + cl.getSignature().fparameters.get(i) + ";"); VarType to = genType.getArguments().get(i); - //System.out.println("(" + from.type + " " + from.value + " -> " + to.type + " " + to.value + ")"); - if(from != null && to != null && to.type == CodeConstants.TYPE_OBJECT) { + if(from != null && to != null /*&& to.type == CodeConstants.TYPE_OBJECT*/) { + //System.out.println("(" + from.type + " " + from + " -> " + to.type + " " + to + ")"); genArgs.put(from, to); } } @@ -428,57 +428,65 @@ else if (isInstanceThis) { TextBuffer buff = new TextBuffer(); boolean ambiguous = setAmbiguousParameters.get(i); VarType type = descriptor.params[i]; - VarType newType = null; - if(desc != null && desc.getSignature() != null && genericArgs.size() != 0 && desc.getSignature().params.size() == lstParameters.size()) { + VarType newType = null; + if(desc != null && desc.getSignature() != null && /*genericArgs.size() != 0 && */desc.getSignature().params.size() == lstParameters.size()) { newType = desc.getSignature().params.get(i); - boolean free = false; + /*boolean free = false; for(String param : desc.getSignature().fparameters) { if(param.equals(newType.value)) { free = true; break; } } - if(!free) { + + if(!free) {*/ type = newType; - } + //} } - if(genArgs.containsKey(type)) { + //System.out.println("check: " + type + " " + type.remap(genArgs) + " " + type.isGeneric() + " " + type.remap(genArgs).isGeneric()); + /*if(genArgs.containsKey(type)) { type = genArgs.get(type); + }*/ + VarType remappedType = type.remap(genArgs); + if(type != remappedType) { + type = remappedType; + } + else if(desc != null && desc.getSignature() != null && genericArgs.size() != 0) { + Map genMap = new HashMap(); + for(int j = 0; j < genericArgs.size(); j++) { + VarType from = GenericType.parse("T" + desc.getSignature().fparameters.get(j) + ";"); + VarType to = genericArgs.get(j); + genMap.put(from, to); + //System.out.println("map: (" + from + " -> " + to + ")"); + } + type = type.remap(genMap); + /*if(genMap.containsKey(type)) { + type = genMap.get(type); + } + // this only checks 1 level deep right now + else if(type.isGeneric()) { + GenericType genType = (GenericType)type; + List toArgs = new ArrayList(); + boolean changed = false; + VarType parent = genType.getParent(); + if(genMap.containsKey(parent)) { + parent = genMap.get(parent); + changed = true; + } + for(VarType arg : genType.getArguments()) { + if(genMap.containsKey(arg)) { + toArgs.add(genMap.get(arg)); + changed = true; + } else { + toArgs.add(arg); + } + } + System.out.println("gen: " + changed + " " + parent + " "); + if(changed) { + type = new GenericType(type.type, type.arrayDim, type.value, parent, toArgs, genType.getWildcard()); + } + }*/ } - else if(desc != null && desc.getSignature() != null && genericArgs.size() != 0) { - Map genMap = new HashMap(); - for(int j = 0; j < genericArgs.size(); j++) { - VarType from = GenericType.parse("T" + desc.getSignature().fparameters.get(j) + ";"); - VarType to = genericArgs.get(j); - genMap.put(from, to); - //System.out.println("map: (" + from + " -> " + to + ")"); - } - if(genMap.containsKey(type)) { - type = genMap.get(type); - } - // this only checks 1 level deep right now - else if(type.isGeneric()) { - GenericType genType = (GenericType)type; - List toArgs = new ArrayList(); - boolean changed = false; - VarType parent = genType.getParent(); - if(genMap.containsKey(parent)) { - parent = genMap.get(parent); - changed = true; - } - for(VarType arg : genType.getArguments()) { - if(genMap.containsKey(arg)) { - toArgs.add(genMap.get(arg)); - changed = true; - } else { - toArgs.add(arg); - } - } - if(changed) { - type = new GenericType(type.type, type.arrayDim, type.value, parent, toArgs, genType.getWildcard()); - } - } - } /*if(desc != null && desc.getSignature() != null) { for(String ps: desc.getSignature().fparameters) { VarType param = GenericType.parse("T" + ps + ";"); @@ -488,12 +496,12 @@ else if(type.isGeneric()) { } } }*/ - VarType exprType = lstParameters.get(i).getInferredExprType(type); - if(exprType != null && exprType.type != CodeConstants.TYPE_NULL && type != null && type.type == CodeConstants.TYPE_GENVAR) { - type = exprType; - } - //System.out.println("param: " + i + " " + newType + " " + exprType + " " + type + " " + lstParameters.get(i)); - ExprProcessor.getCastedExprent(lstParameters.get(i), type, buff, indent, true, ambiguous, tracer); + VarType exprType = lstParameters.get(i).getInferredExprType(type); + if(exprType != null /*&& exprType.type != CodeConstants.TYPE_NULL*/ && type != null && type.type == CodeConstants.TYPE_GENVAR) { + type = exprType; + } + //System.out.println("param: " + i + " " + newType + " " + exprType + " " + type + " " + lstParameters.get(i)); + ExprProcessor.getCastedExprent(lstParameters.get(i), type, buff, indent, type.type != CodeConstants.TYPE_NULL, ambiguous, tracer); buf.append(buff); firstParameter = false; diff --git a/src/org/jetbrains/java/decompiler/struct/gen/VarType.java b/src/org/jetbrains/java/decompiler/struct/gen/VarType.java index af06b8e..81342ed 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/VarType.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/VarType.java @@ -19,6 +19,8 @@ import org.jetbrains.java.decompiler.struct.gen.generics.GenericType; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.util.Map; + public class VarType { // TODO: optimize switch public static final VarType[] EMPTY_ARRAY = {}; @@ -428,4 +430,11 @@ public static int getType(char c) { throw new IllegalArgumentException("Invalid type: " + c); } } + + public VarType remap(Map map) { + if(map.containsKey(this)) { + return map.get(this); + } + return this; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java index 9319bf7..9ad5a1f 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; public class GenericType extends VarType { @@ -326,16 +327,47 @@ else if (par.isGeneric()) { @Override public String toString() { StringBuilder buf = new StringBuilder(); - switch(getWildcard()) { + switch(getWildcard()) { case GenericType.WILDCARD_EXTENDS: buf.append("? extends "); break; case GenericType.WILDCARD_SUPER: buf.append("? super "); break; - } - buf.append(super.toString()); + } + buf.append(super.toString()); buf.append(getTypeArguments()); - return buf.toString(); + return buf.toString(); + } + + @Override + public VarType remap(Map map) { + VarType main = super.remap(map); + if(main != this) { + return main; + } + boolean changed = false; + VarType parent = getParent(); + if(map.containsKey(parent)) { + parent = map.get(parent); + changed = true; + } + List newArgs = new ArrayList(); + for(VarType arg : getArguments()) { + VarType newArg = null; + if(arg != null) { + newArg = arg.remap(map); + } + if(newArg != arg) { + newArgs.add(newArg); + changed = true; + } else { + newArgs.add(arg); + } + } + if(changed) { + return new GenericType(main.type, main.arrayDim, main.value, parent, newArgs, getWildcard()); + } + return this; } } From 497af514fd5c868021fc9be12acac2c387f5541d Mon Sep 17 00:00:00 2001 From: RainWarrior Date: Wed, 14 Oct 2015 15:30:51 +0300 Subject: [PATCH 63/73] handle generic lists with multiple arguments in method invocations --- .../decompiler/exps/InvocationExprent.java | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java index 4c4b898..f98c822 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java @@ -208,14 +208,19 @@ public VarType getInferredExprType(VarType upperBound) { List leftArgs = ((GenericType)upperBound).getArguments(); List rightArgs = ((GenericType)ret).getArguments(); List fparams = desc.getSignature().fparameters; - // trivial case only for now - if(leftArgs.size() == 1 && rightArgs.size() == 1 && fparams.size() == 1) { - VarType l = leftArgs.get(0); - VarType r = rightArgs.get(0); - if(l != null /*&& l.type == CodeConstants.TYPE_OBJECT && !l.equals(r)*/ && r.value.equals(fparams.get(0))) { - genericArgs.add(leftArgs.get(0)); - //type = upperBound; - System.out.println("4: " + leftArgs.get(0) + " " + rightArgs.get(0) + " " + fparams.get(0)); + if(leftArgs.size() == rightArgs.size() && rightArgs.size() == fparams.size()) { + for(int i = 0; i < leftArgs.size(); i++) { + VarType l = leftArgs.get(i); + VarType r = rightArgs.get(i); + if(l != null /*&& l.type == CodeConstants.TYPE_OBJECT && !l.equals(r)*/ && r.value.equals(fparams.get(i))) { + genericArgs.add(l); + //type = upperBound; + System.out.println("4: " + i + " " + l + " " + r + " " + fparams.get(i)); + } + else { + genericArgs.clear(); + break; + } } } } From b14e7fc85e9f8c07da66accb2f490175fbf054ed Mon Sep 17 00:00:00 2001 From: RainWarrior Date: Thu, 15 Oct 2015 01:00:20 +0300 Subject: [PATCH 64/73] using the generic info from InvocationExprent correctly, 1 more error down --- .../decompiler/exps/InvocationExprent.java | 102 ++++++++++-------- 1 file changed, 55 insertions(+), 47 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java index f98c822..b2c3574 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java @@ -174,59 +174,67 @@ public VarType getInferredExprType(VarType upperBound) { VarType type = getExprType(); - List toAdd = new ArrayList(); - if(upperBound != null/* && upperBound.type == CodeConstants.TYPE_OBJECT && descriptor.ret.type == CodeConstants.TYPE_OBJECT*/) { - genericArgs.clear(); - //System.out.println("0: " + isStatic + " " + desc + " " + upperBound + " " + descriptor.ret + " " + upperBound.isGeneric()); - /*if(desc == null) { - // more harn than gain - // Object -> String - if(descriptor.ret.value.equals("java/lang/Object") && !descriptor.ret.equals(upperBound)) { - genericArgs.add(upperBound); - System.out.println("1: " + upperBound + " " + descriptor.ret); - } - // List -> List - if(upperBound.isGeneric()) { - List leftArgs = ((GenericType)upperBound).getArguments(); - //System.out.println("22: " + upperBound + " " + leftArgs.get(0)); - if(leftArgs.size() == 1 && descriptor.ret.equals(upperBound) && leftArgs.get(0).type == CodeConstants.TYPE_OBJECT) { - genericArgs.add(leftArgs.get(0)); - System.out.println("2: " + upperBound.type + " " + upperBound + " " + leftArgs.get(0).type + " " + leftArgs.get(0)); - } + genericArgs.clear(); + + //System.out.println("0: " + isStatic + " " + desc + " " + upperBound + " " + descriptor.ret + " " + upperBound.isGeneric()); + /*if(desc == null) { + // more harn than gain + // Object -> String + if(descriptor.ret.value.equals("java/lang/Object") && !descriptor.ret.equals(upperBound)) { + genericArgs.add(upperBound); + System.out.println("1: " + upperBound + " " + descriptor.ret); + } + // List -> List + if(upperBound.isGeneric()) { + List leftArgs = ((GenericType)upperBound).getArguments(); + //System.out.println("22: " + upperBound + " " + leftArgs.get(0)); + if(leftArgs.size() == 1 && descriptor.ret.equals(upperBound) && leftArgs.get(0).type == CodeConstants.TYPE_OBJECT) { + genericArgs.add(leftArgs.get(0)); + System.out.println("2: " + upperBound.type + " " + upperBound + " " + leftArgs.get(0).type + " " + leftArgs.get(0)); } + } + }*/ + if(desc != null && desc.getSignature() != null) { + VarType ret = desc.getSignature().ret; + Map map = new HashMap(); + // more harm than gain + // T -> String + /*if(upperBound != null && desc.getSignature().fparameters.size() == 1 && desc.getSignature().fparameters.get(0).equals(ret.value)) { + genericArgs.add(upperBound); + System.out.println("3: " + upperBound + " " + ret + " " + desc.getSignature().fparameters.get(0)); }*/ - if(desc != null && desc.getSignature() != null) { - VarType ret = desc.getSignature().ret; - // more harm than gain - // T -> String - /*if(desc.getSignature().fparameters.size() == 1 && desc.getSignature().fparameters.get(0).equals(ret.value)) { - genericArgs.add(upperBound); - System.out.println("3: " + upperBound + " " + ret + " " + desc.getSignature().fparameters.get(0)); - }*/ - // List -> List - if(upperBound.isGeneric() && ret.isGeneric()) { - List leftArgs = ((GenericType)upperBound).getArguments(); - List rightArgs = ((GenericType)ret).getArguments(); - List fparams = desc.getSignature().fparameters; - if(leftArgs.size() == rightArgs.size() && rightArgs.size() == fparams.size()) { - for(int i = 0; i < leftArgs.size(); i++) { - VarType l = leftArgs.get(i); - VarType r = rightArgs.get(i); - if(l != null /*&& l.type == CodeConstants.TYPE_OBJECT && !l.equals(r)*/ && r.value.equals(fparams.get(i))) { - genericArgs.add(l); - //type = upperBound; - System.out.println("4: " + i + " " + l + " " + r + " " + fparams.get(i)); - } - else { - genericArgs.clear(); - break; - } + // List -> List + if(upperBound != null && upperBound.isGeneric() && ret.isGeneric()) { + List leftArgs = ((GenericType)upperBound).getArguments(); + List rightArgs = ((GenericType)ret).getArguments(); + List fparams = desc.getSignature().fparameters; + if(leftArgs.size() == rightArgs.size() && rightArgs.size() == fparams.size()) { + for(int i = 0; i < leftArgs.size(); i++) { + VarType l = leftArgs.get(i); + VarType r = rightArgs.get(i); + if(l != null /*&& l.type == CodeConstants.TYPE_OBJECT && !l.equals(r)*/ && r.value.equals(fparams.get(i))) { + genericArgs.add(l); + map.put(r, l); + System.out.println("4: " + i + " " + l + " " + r + " " + fparams.get(i)); + } + else { + genericArgs.clear(); + map.clear(); + break; } } } } - } + //System.out.println("infer map: "+ ret + " -> " + ret.remap(map)); + + if(!map.isEmpty()) { + // remap and return generic type + VarType newType = ret.remap(map); + if(ret != newType) return newType; + } + return ret; + } return type; } @Override @@ -505,7 +513,7 @@ else if(type.isGeneric()) { if(exprType != null /*&& exprType.type != CodeConstants.TYPE_NULL*/ && type != null && type.type == CodeConstants.TYPE_GENVAR) { type = exprType; } - //System.out.println("param: " + i + " " + newType + " " + exprType + " " + type + " " + lstParameters.get(i)); + //System.out.println("param: " + i + " " + exprType + " " + type + " " + lstParameters.get(i)); ExprProcessor.getCastedExprent(lstParameters.get(i), type, buff, indent, type.type != CodeConstants.TYPE_NULL, ambiguous, tracer); buf.append(buff); From c9699a02f50eaa677fc72201e6c4c3581a13744f Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Wed, 14 Oct 2015 16:07:14 -0700 Subject: [PATCH 65/73] Itterator enchanced for loops can also have a 'copy' instruction, so detect those as well. --- .../decompiler/modules/decompiler/MergeHelper.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java index c240daa..d0158ab 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java @@ -491,6 +491,18 @@ private static boolean matchForEach(DoStatement stat) { stat.setIncExprent(holder.getInstance()); preData.getExprents().remove(initExprents[0]); firstData.getExprents().remove(firstDoExprent); + + if (initExprents[1] != null && initExprents[1].getLeft().type == Exprent.EXPRENT_VAR && + holder.getInstance().type == Exprent.EXPRENT_VAR) { + VarExprent copy = (VarExprent)initExprents[1].getLeft(); + VarExprent inc = (VarExprent)holder.getInstance(); + if (copy.getIndex() == inc.getIndex() && copy.getVersion() == inc.getVersion() && !ExprentUtil.isVarReferenced(inc, stat, copy)) { + preData.getExprents().remove(initExprents[1]); + initExprents[1].getRight().addBytecodeOffsets(initExprents[1].bytecode); + initExprents[1].getRight().addBytecodeOffsets(stat.getIncExprent().bytecode); + stat.setIncExprent(initExprents[1].getRight()); + } + } return true; } else if (initExprents[0] != null && initExprents[1] != null && firstDoExprent != null) { From 788d8fdb6346422ce9717694f957b970625072ec Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Wed, 14 Oct 2015 16:40:04 -0700 Subject: [PATCH 66/73] Be a bit more strict in detecting copy var usage. --- .../java/decompiler/modules/decompiler/MergeHelper.java | 2 +- .../decompiler/modules/decompiler/stats/Statement.java | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java index d0158ab..3025737 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java @@ -496,7 +496,7 @@ private static boolean matchForEach(DoStatement stat) { holder.getInstance().type == Exprent.EXPRENT_VAR) { VarExprent copy = (VarExprent)initExprents[1].getLeft(); VarExprent inc = (VarExprent)holder.getInstance(); - if (copy.getIndex() == inc.getIndex() && copy.getVersion() == inc.getVersion() && !ExprentUtil.isVarReferenced(inc, stat, copy)) { + if (copy.getIndex() == inc.getIndex() && copy.getVersion() == inc.getVersion() && !ExprentUtil.isVarReferenced(inc, stat.getTopParent(), copy)) { preData.getExprents().remove(initExprents[1]); initExprents[1].getRight().addBytecodeOffsets(initExprents[1].bytecode); initExprents[1].getRight().addBytecodeOffsets(stat.getIncExprent().bytecode); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java index f71ed84..686001d 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java @@ -837,6 +837,14 @@ public void setParent(Statement parent) { this.parent = parent; } + public Statement getTopParent() { + Statement ret = this; + while (ret.getParent() != null) { + ret = ret.getParent(); + } + return ret; + } + public HashSet getLabelEdges() { // FIXME: why HashSet? return labelEdges; } From 7397a3a93f58c06def0180227486b7a23915b4b4 Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Thu, 15 Oct 2015 00:15:50 -0700 Subject: [PATCH 67/73] Enhance switches on enums. --- .../main/rels/MethodProcessorRunnable.java | 2 +- .../main/rels/NestedClassProcessor.java | 129 ++++++++++++++++++ .../decompiler/stats/SwitchStatement.java | 55 +++++++- .../java/decompiler/struct/StructClass.java | 3 + 4 files changed, 186 insertions(+), 3 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java index 2e1668d..16d7788 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java @@ -296,7 +296,7 @@ private static void printStatement(Statement statement, String indent, VarProces if (statement.type == Statement.TYPE_DO) { System.out.print(" t:"+((DoStatement)statement).getLooptype()); } else if (statement.type == Statement.TYPE_BASICBLOCK) { - System.out.print(" i:"+((BasicBlockStatement)statement).getBlock().toStringOldIndices().replaceAll("\n", ";")); + System.out.print(" i:"+((BasicBlockStatement)statement).getBlock().toStringOldIndices().replaceAll("\n", ";").replaceAll("\r", "")); } System.out.println(); for (StatEdge edge : statement.getAllSuccessorEdges()) diff --git a/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java b/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java index 3378549..12653a4 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java @@ -16,6 +16,7 @@ package org.jetbrains.java.decompiler.main.rels; 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.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; @@ -25,8 +26,11 @@ import org.jetbrains.java.decompiler.modules.decompiler.exps.*; import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph; import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode; +import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; 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.modules.decompiler.vars.LVTVariable; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor; @@ -39,6 +43,8 @@ import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import com.sun.xml.internal.ws.org.objectweb.asm.Opcodes; + import java.util.*; import java.util.Map.Entry; @@ -59,6 +65,10 @@ public void processClass(ClassNode root, ClassNode node) { return; } + if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM)) { + gatherEnumSwitchMaps(node); + } + if (node.type != ClassNode.CLASS_LAMBDA) { computeLocalVarsAndDefinitions(node); @@ -102,6 +112,125 @@ else if (child.type != ClassNode.CLASS_MEMBER || (child.access & CodeConstants.A } } + /** + * When Java introduced Enums they aded the ability to use them in Switch statements. + * This was done in a purely syntax sugar way using the old switch on int methods. + * The compiler creates a synthetic class with a static int array field. + * To support enums changing post compile, It initializes this field with a length of the current enum length. + * And then for every referenced enum value it adds a mapping in the form of: + * try { + * field[Enum.VALUE.ordinal()] = 1; + * } catch (FieldNotFoundException e) {} + * + * If a class has multiple switches on multiple enums, the compiler adds the init and try list to the BEGINNING of the static initalizer. + * But they add the field to the END of the fields list. + */ + private void gatherEnumSwitchMaps(ClassNode node) { + for (ClassNode child : node.nested) { + gatherEnumSwitchMaps(child); + } + + MethodWrapper clinit = node.getWrapper().getMethodWrapper("", "()V"); + if (clinit == null || clinit.root == null || clinit.root.getFirst().type != Statement.TYPE_SEQUENCE) { + return; + } + + final int STATIC_FINAL_SYNTHETIC = Opcodes.ACC_STATIC | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC; + Set potentialFields = new HashSet(); + for (StructField fd : node.classStruct.getFields()) { + if ((fd.getAccessFlags() & STATIC_FINAL_SYNTHETIC) == STATIC_FINAL_SYNTHETIC && "[I".equals(fd.getDescriptor())) { + potentialFields.add(fd.getName()); + } + } + + if (potentialFields.size() == 0) { + return; + } + + SequenceStatement seq = (SequenceStatement)clinit.root.getFirst(); + for (int x = 0; x < seq.getStats().size();) { + Statement stat = seq.getStats().get(x); + if (stat.type != Statement.TYPE_BASICBLOCK || stat.getExprents() == null || stat.getExprents().size() != 1 || stat.getExprents().get(0).type != Exprent.EXPRENT_ASSIGNMENT) { + break; + } + AssignmentExprent ass = (AssignmentExprent)stat.getExprents().get(0); + if (ass.getLeft().type != Exprent.EXPRENT_FIELD || ass.getRight().type != Exprent.EXPRENT_NEW) { + break; + } + FieldExprent mapField = (FieldExprent)ass.getLeft(); + NewExprent _new = ((NewExprent)ass.getRight()); + if (!mapField.getClassname().equals(node.classStruct.qualifiedName) || !potentialFields.contains(mapField.getName()) || + _new.getNewType().type != CodeConstants.TYPE_INT || _new.getNewType().arrayDim != 1 || + _new.getLstDims().size() != 1 || _new.getLstDims().get(0).type != Exprent.EXPRENT_FUNCTION) { + break; + } + FunctionExprent func = (FunctionExprent)_new.getLstDims().get(0); + if (func.getFuncType() != FunctionExprent.FUNCTION_ARRAY_LENGTH || func.getLstOperands().size() != 1 || func.getLstOperands().get(0).type != Exprent.EXPRENT_INVOCATION) { + break; + } + InvocationExprent invoc = (InvocationExprent)func.getLstOperands().get(0); + if (!"values".equals(invoc.getName()) || !("()[L" + invoc.getClassname() + ";").equals(invoc.getStringDescriptor())) { + break; + } + + String fieldName = mapField.getName(); + String enumName = invoc.getClassname(); + Map idToName = new HashMap(); + + boolean replace = false; + int y = x; + while (++y < seq.getStats().size()) { + if (seq.getStats().get(y).type != Statement.TYPE_TRYCATCH) { + break; + } + CatchStatement _try = (CatchStatement)seq.getStats().get(y); + Statement first = _try.getFirst(); + List exprents = first.getExprents(); + if (_try.getVars().size() != 1 || !"java/lang/NoSuchFieldError".equals(_try.getVars().get(0).getVarType().value) || + first.type != Statement.TYPE_BASICBLOCK || exprents == null || exprents.size() != 1 || exprents.get(0).type != Exprent.EXPRENT_ASSIGNMENT) { + break; + } + ass = (AssignmentExprent)exprents.get(0); + if (ass.getRight().type != Exprent.EXPRENT_CONST || (!(((ConstExprent)ass.getRight()).getValue() instanceof Integer)) || + ass.getLeft().type != Exprent.EXPRENT_ARRAY){ + break; + } + ArrayExprent array = (ArrayExprent)ass.getLeft(); + if (array.getArray().type != Exprent.EXPRENT_FIELD || !array.getArray().equals(mapField) || array.getIndex().type != Exprent.EXPRENT_INVOCATION) { + break; + } + invoc = (InvocationExprent)array.getIndex(); + if (!enumName.equals(invoc.getClassname()) || !"ordinal".equals(invoc.getName()) || !"()I".equals(invoc.getStringDescriptor()) || + invoc.getInstance().type != Exprent.EXPRENT_FIELD) { + break; + } + + FieldExprent enumField = (FieldExprent)invoc.getInstance(); + if (!enumName.equals(enumField.getClassname()) || !enumField.isStatic()) { + break; + } + + idToName.put((Integer)((ConstExprent)ass.getRight()).getValue(), enumField.getName()); + seq.replaceStatement(_try, getNewEmptyStatement()); + replace = true; + } + + if (replace) { + seq.replaceStatement(seq.getStats().get(x), getNewEmptyStatement()); + node.classStruct.enumSwitchMap.put(fieldName, idToName); + node.getWrapper().getHiddenMembers().add(InterpreterUtil.makeUniqueKey(fieldName, "[I")); + } + x = y; + } + } + + private Statement getNewEmptyStatement() { + BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( + DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); + bstat.setExprents(new ArrayList()); + return bstat; + } + private static void setLambdaVars(ClassNode parent, ClassNode child) { if (child.lambdaInformation.is_method_reference) { // method reference, no code and no parameters diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java index 3e27201..f8eec64 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java @@ -24,10 +24,14 @@ import org.jetbrains.java.decompiler.modules.decompiler.DecHelper; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; +import org.jetbrains.java.decompiler.modules.decompiler.exps.ArrayExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; 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.InvocationExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.SwitchExprent; import org.jetbrains.java.decompiler.modules.decompiler.vars.StartEndPair; +import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.gen.VarType; import java.util.*; @@ -118,7 +122,12 @@ public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { tracer.incrementCurrentSourceLine(); } - buf.appendIndent(indent).append(headexprent.get(0).toJava(indent, tracer)).append(" {").appendLineSeparator(); + //Doesn't seem to be a better place to put it so enhance things here + Map remaps = enhanceHead(headexprent.get(0), buf, indent, tracer); + + if (remaps == null) { + buf.appendIndent(indent).append(headexprent.get(0).toJava(indent, tracer)).append(" {").appendLineSeparator(); + } tracer.incrementCurrentSourceLine(); VarType switch_type = headexprent.get(0).getExprType(); @@ -138,7 +147,14 @@ public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { ConstExprent value = (ConstExprent)values.get(j).copy(); value.setConstType(switch_type); - buf.appendIndent(indent).append("case ").append(value.toJava(indent, tracer)).append(":").appendLineSeparator(); + buf.appendIndent(indent).append("case "); + if (remaps == null) { + buf.append(value.toJava(indent, tracer)); + } + else { + buf.append(remaps.get(value.getValue())); + } + buf.append(":").appendLineSeparator(); tracer.incrementCurrentSourceLine(); } } @@ -152,6 +168,41 @@ public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { return buf; } + private Map enhanceHead(Exprent exprent, TextBuffer buf, int indent, BytecodeMappingTracer tracer) { + + if (exprent.type != Exprent.EXPRENT_SWITCH) return null; + + SwitchExprent swtch = (SwitchExprent)exprent; + if (swtch.getValue().type != Exprent.EXPRENT_ARRAY) return null; + + ArrayExprent array = (ArrayExprent)swtch.getValue(); + if (array.getArray().type != Exprent.EXPRENT_FIELD || array.getIndex().type != Exprent.EXPRENT_INVOCATION) return null; + + FieldExprent field = (FieldExprent)array.getArray(); + InvocationExprent invoc = (InvocationExprent)array.getIndex(); + StructClass cls = DecompilerContext.getStructContext().getClass(field.getClassname()); + if (cls == null || !field.isStatic() || !"ordinal".equals(invoc.getName()) || !"()I".equals(invoc.getStringDescriptor())) return null; + + Map ret = cls.enumSwitchMap.get(field.getName()); + if (ret == null) return null; + + for (List lst : getCaseValues()) { + if (lst != null) { + for (ConstExprent cst : lst) { + if (cst != null && (!(cst.getValue() instanceof Integer) || !ret.containsKey(cst.getValue()))) { + return null; + } + } + } + } + + tracer.addMapping(swtch.bytecode); + tracer.addMapping(field.bytecode); + tracer.addMapping(invoc.bytecode); + buf.appendIndent(indent).append((invoc.getInstance().toJava(indent, tracer).enclose("switch(", ")"))).append(" {").appendLineSeparator(); + return ret; + } + public void initExprents() { SwitchExprent swexpr = (SwitchExprent)first.getExprents().remove(first.getExprents().size() - 1); swexpr.setCaseValues(caseValues); diff --git a/src/org/jetbrains/java/decompiler/struct/StructClass.java b/src/org/jetbrains/java/decompiler/struct/StructClass.java index c0683fc..ab0f118 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructClass.java +++ b/src/org/jetbrains/java/decompiler/struct/StructClass.java @@ -30,6 +30,8 @@ import org.jetbrains.java.decompiler.util.VBStyleCollection; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; /* class_file { @@ -65,6 +67,7 @@ public class StructClass extends StructMember { private final VBStyleCollection fields; private final VBStyleCollection methods; private GenericClassDescriptor signature = null; + public final Map> enumSwitchMap = new HashMap>(); private ConstantPool pool; From 32d7a1464e50520aa4e0c78fce84528448bca249 Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Thu, 15 Oct 2015 01:10:03 -0700 Subject: [PATCH 68/73] Fixed issue where inner class fields would not be replaced by their parent method parameters. --- .../main/rels/NestedClassProcessor.java | 51 ++++++++++++++++--- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java b/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java index 12653a4..6d40b8a 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java @@ -706,8 +706,8 @@ private static void insertLocalVars(final ClassNode parent, final ClassNode chil DirectGraph graph = meth.getOrBuildGraph(); - graph.iterateExprents(new DirectGraph.ExprentIterator() { - public int processExprent(Exprent exprent) { + iterateExprents(graph, new ExprentIteratorWithReplace() { + public Exprent processExprent(Exprent exprent) { if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { AssignmentExprent asexpr = (AssignmentExprent)exprent; @@ -718,7 +718,7 @@ public int processExprent(Exprent exprent) { mapFieldsToNewVars.containsKey(InterpreterUtil.makeUniqueKey(child.classStruct.qualifiedName, InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr .getDescriptor().descriptorString)))) { - return 2; + return null; } //if(fexpr.getClassname().equals(child.classStruct.qualifiedName) && @@ -734,13 +734,13 @@ public int processExprent(Exprent exprent) { if (invexpr.getFunctype() == InvocationExprent.TYP_INIT) { // invocation of the super constructor in an anonymous class child.superInvocation = invexpr; // FIXME: save original names of parameters - return 2; + return null; } } - replaceExprent(exprent); + Exprent ret = replaceExprent(exprent); - return 0; + return ret == null ? exprent : ret; } private Exprent replaceExprent(Exprent exprent) { @@ -1191,4 +1191,43 @@ public int hashCode() { return keyfield.hashCode() + varpaar.hashCode(); } } + + private static interface ExprentIteratorWithReplace { + // null - remove exprent + // ret != exprent - replace exprent with ret + Exprent processExprent(Exprent exprent); + } + + + + private static void iterateExprents(DirectGraph graph, ExprentIteratorWithReplace iter) { + LinkedList stack = new LinkedList(); + stack.add(graph.first); + + HashSet setVisited = new HashSet(); + + while (!stack.isEmpty()) { + + DirectNode node = stack.removeFirst(); + + if (setVisited.contains(node)) { + continue; + } + setVisited.add(node); + + for (int i = 0; i < node.exprents.size(); i++) { + Exprent res = iter.processExprent(node.exprents.get(i)); + + if (res == null) { + node.exprents.remove(i); + i--; + } + else if (res != node.exprents.get(i)) { + node.exprents.set(i, res); + } + } + + stack.addAll(node.succs); + } + } } From cef8ac773526a830398ef753629b6b7611f7bcf9 Mon Sep 17 00:00:00 2001 From: Lex Manos Date: Thu, 15 Oct 2015 15:51:24 -0700 Subject: [PATCH 69/73] Added support for ++ and -- field accessors in inner classes. And fixed inner class type find issue. --- .../main/rels/NestedClassProcessor.java | 4 +-- .../main/rels/NestedMemberAccess.java | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java b/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java index 6d40b8a..1e69d8d 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java @@ -1145,7 +1145,7 @@ private static boolean searchForClass(Exprent exprent, VarType classtype) { res = classname.equals(((InvocationExprent)expr).getClassname()); break; case Exprent.EXPRENT_NEW: - VarType newType = expr.getExprType(); + VarType newType = ((NewExprent)expr).getNewType(); res = newType.type == CodeConstants.TYPE_OBJECT && classname.equals(newType.value); break; case Exprent.EXPRENT_VAR: @@ -1198,8 +1198,6 @@ private static interface ExprentIteratorWithReplace { Exprent processExprent(Exprent exprent); } - - private static void iterateExprents(DirectGraph graph, ExprentIteratorWithReplace iter) { LinkedList stack = new LinkedList(); stack.add(graph.first); diff --git a/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java b/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java index 3fa8128..b4856a6 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java +++ b/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java @@ -40,6 +40,7 @@ public class NestedMemberAccess { private static final int METHOD_ACCESS_FIELD_GET = 2; private static final int METHOD_ACCESS_FIELD_SET = 3; private static final int METHOD_ACCESS_METHOD = 4; + private static final int METHOD_ACCESS_FIELD_INC = 5; private boolean noSynthFlag; private final Map mapMethodType = new HashMap(); @@ -137,6 +138,24 @@ private void computeMethodType(ClassNode node, MethodWrapper method) { } } } + break; + case Exprent.EXPRENT_FUNCTION: + FunctionExprent func = (FunctionExprent)exprCore; + if (func.getLstOperands().size() == 1 && func.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) { + FieldExprent field = (FieldExprent)func.getLstOperands().get(0); + if ((parcount == 1 && !field.isStatic()) || + (parcount == 0 && field.isStatic())) { + if (field.getClassname().equals(node.classStruct.qualifiedName)) { + if (func.getFuncType() == FunctionExprent.FUNCTION_IPP || + func.getFuncType() == FunctionExprent.FUNCTION_PPI || + func.getFuncType() == FunctionExprent.FUNCTION_IMM || + func.getFuncType() == FunctionExprent.FUNCTION_MMI) { + type = METHOD_ACCESS_FIELD_INC; + } + } + } + } + break; } @@ -426,6 +445,14 @@ private Exprent replaceAccessExprent(ClassNode caller, MethodWrapper methdest, I } retexprent = invret; + break; + case METHOD_ACCESS_FIELD_INC: + FunctionExprent func = (FunctionExprent)((ExitExprent)source).getValue().copy(); + FieldExprent field = (FieldExprent)func.getLstOperands().get(0); + if (!field.isStatic()) { + field.replaceExprent(field.getInstance(), invexpr.getLstParameters().get(0)); + } + retexprent = func; } From 188b626d3d18755760de06dedf2c050abb03ed61 Mon Sep 17 00:00:00 2001 From: RainWarrior Date: Fri, 16 Oct 2015 02:06:25 +0300 Subject: [PATCH 70/73] cleanup of InvocationExprent changes --- .../decompiler/exps/InvocationExprent.java | 98 +++---------------- 1 file changed, 11 insertions(+), 87 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java index b2c3574..e615207 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java @@ -170,30 +170,11 @@ public VarType getInferredExprType(VarType upperBound) { List matches = getMatchedDescriptors(); StructMethod desc = null; if(matches.size() == 1) desc = matches.get(0); - //System.out.println("infer: " + instance + " " + classname + "." + name + " " + getExprType() + " " + upperBound + " " + desc); VarType type = getExprType(); genericArgs.clear(); - //System.out.println("0: " + isStatic + " " + desc + " " + upperBound + " " + descriptor.ret + " " + upperBound.isGeneric()); - /*if(desc == null) { - // more harn than gain - // Object -> String - if(descriptor.ret.value.equals("java/lang/Object") && !descriptor.ret.equals(upperBound)) { - genericArgs.add(upperBound); - System.out.println("1: " + upperBound + " " + descriptor.ret); - } - // List -> List - if(upperBound.isGeneric()) { - List leftArgs = ((GenericType)upperBound).getArguments(); - //System.out.println("22: " + upperBound + " " + leftArgs.get(0)); - if(leftArgs.size() == 1 && descriptor.ret.equals(upperBound) && leftArgs.get(0).type == CodeConstants.TYPE_OBJECT) { - genericArgs.add(leftArgs.get(0)); - System.out.println("2: " + upperBound.type + " " + upperBound + " " + leftArgs.get(0).type + " " + leftArgs.get(0)); - } - } - }*/ if(desc != null && desc.getSignature() != null) { VarType ret = desc.getSignature().ret; Map map = new HashMap(); @@ -201,7 +182,6 @@ public VarType getInferredExprType(VarType upperBound) { // T -> String /*if(upperBound != null && desc.getSignature().fparameters.size() == 1 && desc.getSignature().fparameters.get(0).equals(ret.value)) { genericArgs.add(upperBound); - System.out.println("3: " + upperBound + " " + ret + " " + desc.getSignature().fparameters.get(0)); }*/ // List -> List if(upperBound != null && upperBound.isGeneric() && ret.isGeneric()) { @@ -212,10 +192,9 @@ public VarType getInferredExprType(VarType upperBound) { for(int i = 0; i < leftArgs.size(); i++) { VarType l = leftArgs.get(i); VarType r = rightArgs.get(i); - if(l != null /*&& l.type == CodeConstants.TYPE_OBJECT && !l.equals(r)*/ && r.value.equals(fparams.get(i))) { + if(l != null && r.value.equals(fparams.get(i))) { genericArgs.add(l); map.put(r, l); - System.out.println("4: " + i + " " + l + " " + r + " " + fparams.get(i)); } else { genericArgs.clear(); @@ -226,8 +205,6 @@ public VarType getInferredExprType(VarType upperBound) { } } - //System.out.println("infer map: "+ ret + " -> " + ret.remap(map)); - if(!map.isEmpty()) { // remap and return generic type VarType newType = ret.remap(map); @@ -237,6 +214,7 @@ public VarType getInferredExprType(VarType upperBound) { } return type; } + @Override public CheckTypesResult checkExprTypeBounds() { CheckTypesResult result = new CheckTypesResult(); @@ -413,18 +391,14 @@ else if (isInstanceThis) { StructClass cl = DecompilerContext.getStructContext().getClass(classname); Map genArgs = new HashMap(); + // building generic info from the instance if(cl != null && cl.getSignature() != null && instance != null && instance.getInferredExprType(null).isGeneric()) { GenericType genType = (GenericType)instance.getInferredExprType(null); if(genType.getArguments().size() == cl.getSignature().fparameters.size()) { - /*System.out.println("remap: " + classname + "." + name + " " + genType); - if(instance instanceof FieldExprent) { - System.out.println(((FieldExprent)instance).getClassname() + "." + ((FieldExprent)instance).getName()); - }*/ for(int i = 0; i < cl.getSignature().fparameters.size(); i++) { VarType from = GenericType.parse("T" + cl.getSignature().fparameters.get(i) + ";"); VarType to = genType.getArguments().get(i); - if(from != null && to != null /*&& to.type == CodeConstants.TYPE_OBJECT*/) { - //System.out.println("(" + from.type + " " + from + " -> " + to.type + " " + to + ")"); + if(from != null && to != null) { genArgs.put(from, to); } } @@ -441,79 +415,30 @@ else if (isInstanceThis) { TextBuffer buff = new TextBuffer(); boolean ambiguous = setAmbiguousParameters.get(i); VarType type = descriptor.params[i]; - VarType newType = null; - if(desc != null && desc.getSignature() != null && /*genericArgs.size() != 0 && */desc.getSignature().params.size() == lstParameters.size()) { - newType = desc.getSignature().params.get(i); - /*boolean free = false; - for(String param : desc.getSignature().fparameters) { - if(param.equals(newType.value)) { - free = true; - break; - } - } - - if(!free) {*/ - type = newType; - //} + // using info from the generic signature + if(desc != null && desc.getSignature() != null && desc.getSignature().params.size() == lstParameters.size()) { + type = desc.getSignature().params.get(i); } - //System.out.println("check: " + type + " " + type.remap(genArgs) + " " + type.isGeneric() + " " + type.remap(genArgs).isGeneric()); - /*if(genArgs.containsKey(type)) { - type = genArgs.get(type); - }*/ + // applying generic info from the signature VarType remappedType = type.remap(genArgs); if(type != remappedType) { type = remappedType; } + // and from the inferred generic arguments else if(desc != null && desc.getSignature() != null && genericArgs.size() != 0) { Map genMap = new HashMap(); for(int j = 0; j < genericArgs.size(); j++) { VarType from = GenericType.parse("T" + desc.getSignature().fparameters.get(j) + ";"); VarType to = genericArgs.get(j); genMap.put(from, to); - //System.out.println("map: (" + from + " -> " + to + ")"); } type = type.remap(genMap); - /*if(genMap.containsKey(type)) { - type = genMap.get(type); - } - // this only checks 1 level deep right now - else if(type.isGeneric()) { - GenericType genType = (GenericType)type; - List toArgs = new ArrayList(); - boolean changed = false; - VarType parent = genType.getParent(); - if(genMap.containsKey(parent)) { - parent = genMap.get(parent); - changed = true; - } - for(VarType arg : genType.getArguments()) { - if(genMap.containsKey(arg)) { - toArgs.add(genMap.get(arg)); - changed = true; - } else { - toArgs.add(arg); - } - } - System.out.println("gen: " + changed + " " + parent + " "); - if(changed) { - type = new GenericType(type.type, type.arrayDim, type.value, parent, toArgs, genType.getWildcard()); - } - }*/ } - /*if(desc != null && desc.getSignature() != null) { - for(String ps: desc.getSignature().fparameters) { - VarType param = GenericType.parse("T" + ps + ";"); - if(param.equals(type)) { // found free argument, need to infer it from the argument - type = lstParameters.get(i).getExprType(); - break; - } - } - }*/ + // not passing it along if what we get back is more specific VarType exprType = lstParameters.get(i).getInferredExprType(type); - if(exprType != null /*&& exprType.type != CodeConstants.TYPE_NULL*/ && type != null && type.type == CodeConstants.TYPE_GENVAR) { + if(exprType != null && type != null && type.type == CodeConstants.TYPE_GENVAR) { type = exprType; } - //System.out.println("param: " + i + " " + exprType + " " + type + " " + lstParameters.get(i)); ExprProcessor.getCastedExprent(lstParameters.get(i), type, buff, indent, type.type != CodeConstants.TYPE_NULL, ambiguous, tracer); buf.append(buff); @@ -530,7 +455,6 @@ private List getMatchedDescriptors() { List matches = new ArrayList(); StructClass cl = DecompilerContext.getStructContext().getClass(classname); - //System.out.println("m: " + classname + "." + name + " " + cl); if (cl == null) return matches; nextMethod: From 71f348d0c9b66b35db2c7a42d37d15ea7f2ea5d9 Mon Sep 17 00:00:00 2001 From: cpw Date: Fri, 16 Oct 2015 21:39:10 -0400 Subject: [PATCH 71/73] Tweak decompilation test to new settings (no synthetics or bridge) --- .../jetbrains/java/decompiler/MinecraftDecompilationTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/org/jetbrains/java/decompiler/MinecraftDecompilationTest.java b/test/org/jetbrains/java/decompiler/MinecraftDecompilationTest.java index 2afd2e6..087c810 100644 --- a/test/org/jetbrains/java/decompiler/MinecraftDecompilationTest.java +++ b/test/org/jetbrains/java/decompiler/MinecraftDecompilationTest.java @@ -43,10 +43,8 @@ public void setUp() throws IOException { // -din=1 -rbr=0 -dgs=1 -asc=1 -rsy=0 Map mcFFOptions = new HashMap() {{ put(IFernflowerPreferences.DECOMPILE_INNER,"1"); - put(IFernflowerPreferences.REMOVE_BRIDGE, "0"); put(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES,"1"); put(IFernflowerPreferences.ASCII_STRING_CHARACTERS,"1"); - put(IFernflowerPreferences.REMOVE_SYNTHETIC,"0"); put(IFernflowerPreferences.INCLUDE_ENTIRE_CLASSPATH, "1"); }}; fixture.setUp(mcFFOptions); From ded50868b3d9d908540f9c895eca95f44bb0ce9f Mon Sep 17 00:00:00 2001 From: cpw Date: Sat, 17 Oct 2015 00:50:20 -0400 Subject: [PATCH 72/73] Fix constants with trailing zeros and incorrect types. --- .../modules/decompiler/exps/ConstExprent.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java index 3398924..0fc4055 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java @@ -182,16 +182,16 @@ else if (lval == Long.MIN_VALUE) { String doublefield; if (literal) { if (Double.isNaN(dval)) { - return new TextBuffer("0.0D / 0.0"); + return new TextBuffer("0.0D / 0.0D"); } else if (dval == Double.POSITIVE_INFINITY) { - return new TextBuffer("1.0D / 0.0"); + return new TextBuffer("1.0D / 0.0D"); } else if (dval == Double.NEGATIVE_INFINITY) { - return new TextBuffer("-1.0D / 0.0"); + return new TextBuffer("-1.0D / 0.0D"); } else { - return new TextBuffer(value.toString()).append("D"); + return new TextBuffer(trimZeros(value.toString())).append("D"); } } else if (Double.isNaN(dval)) { @@ -219,16 +219,16 @@ else if (dval == Double.MIN_VALUE) { String floatfield; if (literal) { if (Double.isNaN(fval)) { - return new TextBuffer("0.0F / 0.0"); + return new TextBuffer("0.0F / 0.0F"); } else if (fval == Double.POSITIVE_INFINITY) { - return new TextBuffer("1.0F / 0.0"); + return new TextBuffer("1.0F / 0.0F"); } else if (fval == Double.NEGATIVE_INFINITY) { - return new TextBuffer("-1.0F / 0.0"); + return new TextBuffer("-1.0F / 0.0F"); } else { - return new TextBuffer(value.toString()).append("F"); + return new TextBuffer(trimZeros(value.toString())).append("F"); } } else if (Float.isNaN(fval)) { From 36ea9a310b443b45ecdc298060e3685f8541950a Mon Sep 17 00:00:00 2001 From: cpw Date: Sat, 17 Oct 2015 00:51:19 -0400 Subject: [PATCH 73/73] Deterministic label names based on jvm instruction # instead of ID --- .../java/decompiler/modules/decompiler/ExprProcessor.java | 2 +- .../modules/decompiler/stats/CatchAllStatement.java | 2 +- .../decompiler/modules/decompiler/stats/CatchStatement.java | 2 +- .../java/decompiler/modules/decompiler/stats/DoStatement.java | 2 +- .../decompiler/modules/decompiler/stats/GeneralStatement.java | 2 +- .../java/decompiler/modules/decompiler/stats/IfStatement.java | 4 ++-- .../modules/decompiler/stats/SequenceStatement.java | 2 +- .../decompiler/modules/decompiler/stats/SwitchStatement.java | 2 +- .../modules/decompiler/stats/SynchronizedStatement.java | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java index 60832d3..c832772 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java @@ -815,7 +815,7 @@ public static TextBuffer jmpWrapper(Statement stat, int indent, boolean semicolo } if (edge.labeled) { - buf.append(" label").append(edge.closure.id.toString()); + buf.append(" label").append(edge.closure.getStartEndRange().start); } buf.append(";").appendLineSeparator(); tracer.incrementCurrentSourceLine(); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java index 812ed10..34dfa85 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java @@ -120,7 +120,7 @@ public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { boolean labeled = isLabeled(); if (labeled) { - buf.appendIndent(indent).append("label").append(this.id.toString()).append(":").appendLineSeparator(); + buf.appendIndent(indent).append("label").append(this.getStartEndRange().start).append(":").appendLineSeparator(); tracer.incrementCurrentSourceLine(); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java index 8d56042..9359fbb 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java @@ -157,7 +157,7 @@ public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { buf.append(ExprProcessor.listToJava(varDefinitions, indent, tracer)); if (isLabeled()) { - buf.appendIndent(indent).append("label").append(this.id.toString()).append(":").appendLineSeparator(); + buf.appendIndent(indent).append("label").append(this.getStartEndRange().start).append(":").appendLineSeparator(); tracer.incrementCurrentSourceLine(); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java index acc74a6..6636d1d 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java @@ -100,7 +100,7 @@ public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { buf.append(ExprProcessor.listToJava(varDefinitions, indent, tracer)); if (isLabeled()) { - buf.appendIndent(indent).append("label").append(this.id.toString()).append(":").appendLineSeparator(); + buf.appendIndent(indent).append("label").append(this.getStartEndRange().start).append(":").appendLineSeparator(); tracer.incrementCurrentSourceLine(); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/GeneralStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/GeneralStatement.java index 4f22cf7..499894a 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/GeneralStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/GeneralStatement.java @@ -57,7 +57,7 @@ public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { TextBuffer buf = new TextBuffer(); if (isLabeled()) { - buf.appendIndent(indent).append("label").append(this.id.toString()).append(":").appendLineSeparator(); + buf.appendIndent(indent).append("label").append(this.getStartEndRange().start).append(":").appendLineSeparator(); } buf.appendIndent(indent).append("abstract statement {").appendLineSeparator(); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java index 8f3e467..6b0eb9a 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java @@ -213,7 +213,7 @@ public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { buf.append(first.toJava(indent, tracer)); if (isLabeled()) { - buf.appendIndent(indent).append("label").append(this.id.toString()).append(":").appendLineSeparator(); + buf.appendIndent(indent).append("label").append(this.getStartEndRange().start).append(":").appendLineSeparator(); tracer.incrementCurrentSourceLine(); } @@ -234,7 +234,7 @@ public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { } if (ifedge.labeled) { - buf.append(" label").append(ifedge.closure.id.toString()); + buf.append(" label").append(ifedge.closure.getStartEndRange().start); } } buf.append(";").appendLineSeparator(); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java index 7d2ee43..92588fe 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java @@ -105,7 +105,7 @@ public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { buf.append(ExprProcessor.listToJava(varDefinitions, indent, tracer)); if (islabeled) { - buf.appendIndent(indent++).append("label").append(this.id.toString()).append(": {").appendLineSeparator(); + buf.appendIndent(indent++).append("label").append(this.getStartEndRange().start).append(": {").appendLineSeparator(); tracer.incrementCurrentSourceLine(); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java index f8eec64..2a29309 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java @@ -118,7 +118,7 @@ public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { buf.append(first.toJava(indent, tracer)); if (isLabeled()) { - buf.appendIndent(indent).append("label").append(this.id.toString()).append(":").appendLineSeparator(); + buf.appendIndent(indent).append("label").append(this.getStartEndRange().start).append(":").appendLineSeparator(); tracer.incrementCurrentSourceLine(); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SynchronizedStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SynchronizedStatement.java index b71531f..aaafab9 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SynchronizedStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SynchronizedStatement.java @@ -76,7 +76,7 @@ public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { buf.append(first.toJava(indent, tracer)); if (isLabeled()) { - buf.appendIndent(indent).append("label").append(this.id.toString()).append(":").appendLineSeparator(); + buf.appendIndent(indent).append("label").append(this.getStartEndRange().start).append(":").appendLineSeparator(); tracer.incrementCurrentSourceLine(); }