From 32e7765874c408c9ba7eb680ed9fbc68d7b165dc Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Fri, 17 Oct 2014 15:41:17 +0400 Subject: [PATCH] decompiler: fixed bytecode mapping for anonymous classes --- .../java/decompiler/main/ClassWriter.java | 64 +++++++++--------- .../decompiler/main/ClassesProcessor.java | 2 +- .../java/decompiler/main/TextBuffer.java | 10 ++- .../modules/decompiler/exps/NewExprent.java | 4 +- .../modules/decompiler/exps/VarExprent.java | 3 +- .../TestClassSimpleBytecodeMapping$1.class | Bin 0 -> 698 bytes .../pkg/TestClassSimpleBytecodeMapping.class | Bin 675 -> 838 bytes .../TestClassSimpleBytecodeMapping.dec | 49 ++++++++++---- .../pkg/TestClassSimpleBytecodeMapping.java | 15 +++- 9 files changed, 95 insertions(+), 52 deletions(-) create mode 100644 testData/classes/pkg/TestClassSimpleBytecodeMapping$1.class diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index feb8093..9fcf4bb 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -43,10 +43,7 @@ import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.struct.gen.generics.*; import org.jetbrains.java.decompiler.util.InterpreterUtil; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; public class ClassWriter { @@ -160,11 +157,11 @@ public class ClassWriter { DecompilerContext.getLogger().endWriteClass(); } - public void classToJava(ClassNode node, TextBuffer buffer, int indent) { + public void classToJava(ClassNode node, TextBuffer buffer, int indent, BytecodeMappingTracer tracer) { ClassNode outerNode = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE); DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, node); - int total_offset_lines = 0; + final int startLine = tracer != null ? tracer.getCurrentSourceLine() : 0; BytecodeMappingTracer dummy_tracer = new BytecodeMappingTracer(); try { @@ -176,14 +173,12 @@ public class ClassWriter { DecompilerContext.getLogger().startWriteClass(cl.qualifiedName); - String lineSeparator = DecompilerContext.getNewLineSeparator(); - // write class definition int start_class_def = buffer.length(); writeClassDefinition(node, buffer, indent); // // count lines in class definition the easiest way -// total_offset_lines = buffer.substring(start_class_def).toString().split(lineSeparator, -1).length - 1; +// startLine = buffer.substring(start_class_def).toString().split(lineSeparator, -1).length - 1; boolean hasContent = false; @@ -199,14 +194,14 @@ public class ClassWriter { if (isEnum) { if (enumFields) { buffer.append(','); - buffer.append(lineSeparator); + buffer.appendLineSeparator(); } enumFields = true; } else if (enumFields) { buffer.append(';'); - buffer.append(lineSeparator); - buffer.append(lineSeparator); + buffer.appendLineSeparator(); + buffer.appendLineSeparator(); enumFields = false; } @@ -217,11 +212,11 @@ public class ClassWriter { if (enumFields) { buffer.append(';'); - buffer.append(lineSeparator); + buffer.appendLineSeparator(); } // FIXME: fields don't matter at the moment - total_offset_lines = buffer.count(lineSeparator, start_class_def); + //startLine = buffer.countLines(start_class_def); // methods for (StructMethod mt : cl.getMethods()) { @@ -232,15 +227,15 @@ public class ClassWriter { int position = buffer.length(); if (hasContent) { - buffer.append(lineSeparator); + buffer.appendLineSeparator(); } - BytecodeMappingTracer method_tracer = new BytecodeMappingTracer(total_offset_lines); + BytecodeMappingTracer method_tracer = new BytecodeMappingTracer(buffer.countLines() + startLine); boolean methodSkipped = !methodToJava(node, mt, buffer, indent + 1, method_tracer); if (!methodSkipped) { hasContent = true; DecompilerContext.getBytecodeSourceMapper().addTracer(cl.qualifiedName, InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()), method_tracer); - total_offset_lines = (method_tracer.getCurrentSourceLine() + 1); // zero-based line index + //startLine = (method_tracer.getCurrentSourceLine() + 1); // zero-based line index } else { buffer.setLength(position); @@ -257,9 +252,9 @@ public class ClassWriter { if (hide) continue; if (hasContent) { - buffer.append(lineSeparator); + buffer.appendLineSeparator(); } - classToJava(inner, buffer, indent + 1); + classToJava(inner, buffer, indent + 1, tracer); hasContent = true; } @@ -268,7 +263,7 @@ public class ClassWriter { buffer.appendIndent(indent).append('}'); if (node.type != ClassNode.CLASS_ANONYMOUS) { - buffer.append(lineSeparator); + buffer.appendLineSeparator(); } } finally { @@ -794,7 +789,7 @@ public class ClassWriter { } buffer.append(';'); - buffer.append(lineSeparator); + buffer.appendLineSeparator(); } else { if (!clinit && !dinit) { @@ -811,7 +806,7 @@ public class ClassWriter { if (root != null && !methodWrapper.decompiledWithErrors) { // check for existence try { - tracer.incrementCurrentSourceLine(buffer.count(lineSeparator, start_index_method)); + tracer.incrementCurrentSourceLine(buffer.countLines(start_index_method)); int startLine = tracer.getCurrentSourceLine(); TextBuffer code = root.toJava(indent + 1, tracer); @@ -845,31 +840,36 @@ public class ClassWriter { // save total lines // TODO: optimize - tracer.setCurrentSourceLine(buffer.count(lineSeparator, start_index_method)); + tracer.setCurrentSourceLine(buffer.countLines(start_index_method)); return !hideMethod; } private void mapLines(TextBuffer code, StructLineNumberTableAttribute table, BytecodeMappingTracer tracer, int startLine) { // build line start offsets map - HashMap lineStartOffsets = new HashMap(); + HashMap> lineStartOffsets = new HashMap>(); for (Map.Entry entry : tracer.getMapping().entrySet()) { Integer lineNumber = entry.getValue() - startLine; - Integer curr = lineStartOffsets.get(lineNumber); - if (curr == null || curr > entry.getKey()) { - lineStartOffsets.put(lineNumber, entry.getKey()); + Set curr = lineStartOffsets.get(lineNumber); + if (curr == null) { + curr = new TreeSet(); // requires natural sorting! } + curr.add(entry.getKey()); + lineStartOffsets.put(lineNumber, curr); } String lineSeparator = DecompilerContext.getNewLineSeparator(); StringBuilder text = code.getOriginalText(); int pos = text.indexOf(lineSeparator); int lineNumber = 0; while (pos != -1) { - Integer startOffset = lineStartOffsets.get(lineNumber); - if (startOffset != null) { - int number = table.findLineNumber(startOffset); - if (number >= 0) { - code.setLineMapping(number, pos); + Set startOffsets = lineStartOffsets.get(lineNumber); + if (startOffsets != null) { + for (Integer offset : startOffsets) { + int number = table.findLineNumber(offset); + if (number >= 0) { + code.setLineMapping(number, pos); + break; + } } } pos = text.indexOf(lineSeparator, pos+1); diff --git a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java index 262b825..3fe0e01 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java @@ -256,7 +256,7 @@ public class ClassesProcessor { new NestedMemberAccess().propagateMemberAccess(root); TextBuffer classBuffer = new TextBuffer(AVERAGE_CLASS_SIZE); - new ClassWriter().classToJava(root, classBuffer, 0); + new ClassWriter().classToJava(root, classBuffer, 0, null); String lineSeparator = DecompilerContext.getNewLineSeparator(); int total_offset_lines = 0; diff --git a/src/org/jetbrains/java/decompiler/main/TextBuffer.java b/src/org/jetbrains/java/decompiler/main/TextBuffer.java index e631a51..b952811 100644 --- a/src/org/jetbrains/java/decompiler/main/TextBuffer.java +++ b/src/org/jetbrains/java/decompiler/main/TextBuffer.java @@ -115,7 +115,7 @@ public class TextBuffer { while (currentLine < srcLines.length) { String line = srcLines[currentLine]; int lineEnd = currentLineStartOffset + line.length() + myLineSeparator.length(); - if (markOffset >= currentLineStartOffset && markOffset <= lineEnd) { + if (markOffset <= lineEnd) { int requiredLine = markLine - 1; int linesToAdd = requiredLine - dumpedLines; dumpedLines = requiredLine; @@ -223,6 +223,14 @@ public class TextBuffer { return this; } + public int countLines() { + return countLines(0); + } + + public int countLines(int from) { + return count(myLineSeparator, from); + } + public int count(String substring, int from) { int count = 0, length = substring.length(), p = from; while ((p = myStringBuilder.indexOf(substring, p)) > 0) { 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 6178ee4..a810a21 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java @@ -270,9 +270,11 @@ public class NewExprent extends Exprent { } Exprent methodObject = constructor == null ? null : constructor.getInstance(); new ClassWriter().classLambdaToJava(child, buf, methodObject, indent); + tracer.incrementCurrentSourceLine(buf.countLines()); } else { - new ClassWriter().classToJava(child, buf, indent); + new ClassWriter().classToJava(child, buf, indent, tracer); + tracer.incrementCurrentSourceLine(buf.countLines()); } } else if (directArrayInit) { 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 60a4efa..1910464 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java @@ -17,7 +17,6 @@ package org.jetbrains.java.decompiler.modules.decompiler.exps; import java.util.ArrayList; import java.util.List; -import java.util.Set; import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.ClassWriter; @@ -92,7 +91,7 @@ public class VarExprent extends Exprent { if (classdef) { ClassNode child = DecompilerContext.getClassProcessor().getMapRootClasses().get(vartype.value); - new ClassWriter().classToJava(child, buffer, indent); + new ClassWriter().classToJava(child, buffer, indent, tracer); } else { String name = null; diff --git a/testData/classes/pkg/TestClassSimpleBytecodeMapping$1.class b/testData/classes/pkg/TestClassSimpleBytecodeMapping$1.class new file mode 100644 index 0000000000000000000000000000000000000000..93a9cb210c09fbc93509e90dc22be1b348cbcc66 GIT binary patch literal 698 zcma)4T~8B16g|_H4qaAV3KbL;ECt${SWS4t@F0PNq^Plk_idS!f$i*Ocb0^o1&NyY z;1BRe8PBW`Vq%0%cJ7^X&;6J)^YhpD9{@Jdt)Ys?9wZiOSj3ZQJoT`IXA;jPmL*mM zs`f-@tv3R#esVVM9jeUkL}8W<^dyPY`wOc^@v%AxlSG^G7WwbA(RN#4qkW%*&XGW6 zhZO`K^|etS^2t!8hv6_HQciQjwe3jWAkNc~+S4wu`u{OrpN8il3+|dx6la|4K-rV{ zSmLFRCRTma;Q5$AAkp%%hIN7I&$%(KtHg$nHaa};-REt+74Y|sQE8!x$^-(}yBCFK z-1|5@RU<1}RtMcF?vueUXoHN|$7Y%gW`;=;92R2pVgjX^5 z4_H7ZjucVYM*B}tF)j;`4L?6*T{Bg?F!|30VK2md)xYW0s^6y*b(n=Fz|ldLEOjwETVp DEmWj} literal 0 HcmV?d00001 diff --git a/testData/classes/pkg/TestClassSimpleBytecodeMapping.class b/testData/classes/pkg/TestClassSimpleBytecodeMapping.class index 1313994dfa069c3433c8fc4d216502d1d754329d..4e4f8fbe16f8cdbe2322c0edac4f018a2fe316ce 100644 GIT binary patch literal 838 zcmah{O>fgc5Ph4kbzLV7Eg?|)osc>yBrOy!MI0zXs+0mE6%t1$U4mO@9XSqCe+f5! zK`(G9UlKonABA{pic&7rhnd+oGjGRl_UEr}KLBjwfsH)or*IPs78Y$RVOe6uMi#3! z9IV->plackh1xXM;Y!pc8UoIq)+*W!ywOOF1ac33?Z=M<(r*2gKxQ{QR01Xsp)xrVaDT?hBDEg;w|wj}NwyeP1CIgvnd@>|clN2*61E*(%{xnq`kt^BE6u`HaIr~Cj!nMN1CXk;bR4>0_$U) zsyB;ds)_?Sjg>%WoJBI8$U@tE`RJG37VO;|SK4(Jyeo8SLQ^Q2j67W97pCfcR#5eI+;Gtq*!WAUkDIt9;O2HZP6;^p zjaY4iaNN;oIBDVzwq4v6Si9KOzBETZde|XXA=ASwskFcJa8E!y_V9r2i9jnfGMaPY z%cV5KY*9q&m9}f!`S*PuSvqWk$UH1iW5ff{y37K4LBFzl2JdrKZqnK^A90D+U&cOK z%%~shw03w7r>gb8pxwFp6 0.0D) { System.out.println("0"); return 0; @@ -11,26 +16,42 @@ public class TestClassSimpleBytecodeMapping { return 1; } } + + void run(Runnable var1) { + var1.run(); + } } class pkg/TestClassSimpleBytecodeMapping{ + method run (Ljava/lang/Runnable;)V{ + 1 20 + } + method test ()I{ 0 4 3 4 5 4 - 8 5 - b 5 - c 5 - d 5 - 10 6 - 13 6 - 15 6 - 18 7 - 19 7 - 1a 9 - 1d 9 - 1f 9 - 22 10 - 23 10 + 11 5 + 14 10 + 17 10 + 18 10 + 19 10 + 1c 11 + 1f 11 + 21 11 + 24 12 + 25 12 + 26 14 + 29 14 + 2b 14 + 2e 15 + 2f 15 + } +} +class pkg/TestClassSimpleBytecodeMapping$1{ + method run ()V{ + 0 7 + 3 7 + 5 7 } } diff --git a/testData/src/pkg/TestClassSimpleBytecodeMapping.java b/testData/src/pkg/TestClassSimpleBytecodeMapping.java index 8233fa9..0e9ecc2 100644 --- a/testData/src/pkg/TestClassSimpleBytecodeMapping.java +++ b/testData/src/pkg/TestClassSimpleBytecodeMapping.java @@ -1,5 +1,8 @@ package pkg; +import java.lang.Override; +import java.lang.Runnable; + public class TestClassSimpleBytecodeMapping { public TestClassSimpleBytecodeMapping() {} @@ -7,7 +10,14 @@ public class TestClassSimpleBytecodeMapping { public int test() { System.out.println("before"); - + + run(new Runnable() { + @Override + public void run() { + System.out.println("Runnable"); + } + }); + if(Math.random() > 0) { System.out.println("0"); return 0; @@ -17,4 +27,7 @@ public class TestClassSimpleBytecodeMapping { } } + void run(Runnable r) { + r.run(); + } }