diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index acb25db..feb8093 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -43,6 +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; @@ -581,13 +582,9 @@ public class ClassWriter { boolean isDeprecated = mt.getAttributes().containsKey("Deprecated"); boolean clinit = false, init = false, dinit = false; - int startLine = -1; + StructLineNumberTableAttribute lineNumberTable = null; if (DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_LINE_NUMBERS)) { - StructLineNumberTableAttribute lineNumberTable = - (StructLineNumberTableAttribute)mt.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_LINE_NUMBER_TABLE); - if (lineNumberTable != null) { - startLine = lineNumberTable.getFirstLine(); - } + lineNumberTable = (StructLineNumberTableAttribute)mt.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_LINE_NUMBER_TABLE); } MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); @@ -804,8 +801,10 @@ public class ClassWriter { buffer.append(' '); } - //TODO: for now only start line set - buffer.setCurrentLine(startLine-1); + // We do not have line information for method start, lets have it here for now + if (lineNumberTable != null) { + buffer.setCurrentLine(lineNumberTable.getFirstLine() - 1); + } buffer.append('{').appendLineSeparator(); RootStatement root = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root; @@ -813,11 +812,16 @@ public class ClassWriter { if (root != null && !methodWrapper.decompiledWithErrors) { // check for existence try { tracer.incrementCurrentSourceLine(buffer.count(lineSeparator, start_index_method)); + int startLine = tracer.getCurrentSourceLine(); TextBuffer code = root.toJava(indent + 1, tracer); hideMethod = (clinit || dinit || hideConstructor(wrapper, init, throwsExceptions, paramCount)) && code.length() == 0; + if (!hideMethod && lineNumberTable != null) { + mapLines(code, lineNumberTable, tracer, startLine); + } + buffer.append(code); } catch (Throwable ex) { @@ -846,6 +850,33 @@ public class ClassWriter { return !hideMethod; } + private void mapLines(TextBuffer code, StructLineNumberTableAttribute table, BytecodeMappingTracer tracer, int startLine) { + // build line start offsets map + 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()); + } + } + 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); + } + } + pos = text.indexOf(lineSeparator, pos+1); + lineNumber++; + } + } + private static boolean hideConstructor(ClassWrapper wrapper, boolean init, boolean throwsExceptions, int paramCount) { if (!init || throwsExceptions || paramCount > 0 || !DecompilerContext.getOption(IFernflowerPreferences.HIDE_DEFAULT_CONSTRUCTOR)) { return false; diff --git a/src/org/jetbrains/java/decompiler/main/TextBuffer.java b/src/org/jetbrains/java/decompiler/main/TextBuffer.java index 45d29c3..e631a51 100644 --- a/src/org/jetbrains/java/decompiler/main/TextBuffer.java +++ b/src/org/jetbrains/java/decompiler/main/TextBuffer.java @@ -43,9 +43,13 @@ public class TextBuffer { } public void setCurrentLine(int line) { + setLineMapping(line, myStringBuilder.length()+1); + } + + public void setLineMapping(int line, int offset) { if (line >= 0) { checkMapCreated(); - myLineToOffsetMapping.put(line, myStringBuilder.length()+1); + myLineToOffsetMapping.put(line, offset); } } @@ -257,4 +261,8 @@ public class TextBuffer { } return res; } + + public StringBuilder getOriginalText() { + return myStringBuilder; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructLineNumberTableAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructLineNumberTableAttribute.java index f35d3d9..7149485 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructLineNumberTableAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructLineNumberTableAttribute.java @@ -54,9 +54,11 @@ public class StructLineNumberTableAttribute extends StructGeneralAttribute { } public int findLineNumber(int pc) { - for (int i = 0; i < myLineInfo.length; i += 2) { - if (pc >= myLineInfo[i]) { - return myLineInfo[i + 1]; + if (myLineInfo.length >= 2) { + for (int i = myLineInfo.length - 2; i >= 0; i -= 2) { + if (pc >= myLineInfo[i]) { + return myLineInfo[i + 1]; + } } } return -1;