mirror of
https://github.com/moparisthebest/fernflower
synced 2024-11-29 04:22:22 -05:00
decompiler: fixed bytecode mapping for anonymous classes
This commit is contained in:
parent
b3681fe952
commit
32e7765874
@ -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.struct.gen.generics.*;
|
||||||
import org.jetbrains.java.decompiler.util.InterpreterUtil;
|
import org.jetbrains.java.decompiler.util.InterpreterUtil;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.*;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class ClassWriter {
|
public class ClassWriter {
|
||||||
|
|
||||||
@ -160,11 +157,11 @@ public class ClassWriter {
|
|||||||
DecompilerContext.getLogger().endWriteClass();
|
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);
|
ClassNode outerNode = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE);
|
||||||
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, 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();
|
BytecodeMappingTracer dummy_tracer = new BytecodeMappingTracer();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -176,14 +173,12 @@ public class ClassWriter {
|
|||||||
|
|
||||||
DecompilerContext.getLogger().startWriteClass(cl.qualifiedName);
|
DecompilerContext.getLogger().startWriteClass(cl.qualifiedName);
|
||||||
|
|
||||||
String lineSeparator = DecompilerContext.getNewLineSeparator();
|
|
||||||
|
|
||||||
// write class definition
|
// write class definition
|
||||||
int start_class_def = buffer.length();
|
int start_class_def = buffer.length();
|
||||||
writeClassDefinition(node, buffer, indent);
|
writeClassDefinition(node, buffer, indent);
|
||||||
|
|
||||||
// // count lines in class definition the easiest way
|
// // 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;
|
boolean hasContent = false;
|
||||||
|
|
||||||
@ -199,14 +194,14 @@ public class ClassWriter {
|
|||||||
if (isEnum) {
|
if (isEnum) {
|
||||||
if (enumFields) {
|
if (enumFields) {
|
||||||
buffer.append(',');
|
buffer.append(',');
|
||||||
buffer.append(lineSeparator);
|
buffer.appendLineSeparator();
|
||||||
}
|
}
|
||||||
enumFields = true;
|
enumFields = true;
|
||||||
}
|
}
|
||||||
else if (enumFields) {
|
else if (enumFields) {
|
||||||
buffer.append(';');
|
buffer.append(';');
|
||||||
buffer.append(lineSeparator);
|
buffer.appendLineSeparator();
|
||||||
buffer.append(lineSeparator);
|
buffer.appendLineSeparator();
|
||||||
enumFields = false;
|
enumFields = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,11 +212,11 @@ public class ClassWriter {
|
|||||||
|
|
||||||
if (enumFields) {
|
if (enumFields) {
|
||||||
buffer.append(';');
|
buffer.append(';');
|
||||||
buffer.append(lineSeparator);
|
buffer.appendLineSeparator();
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: fields don't matter at the moment
|
// FIXME: fields don't matter at the moment
|
||||||
total_offset_lines = buffer.count(lineSeparator, start_class_def);
|
//startLine = buffer.countLines(start_class_def);
|
||||||
|
|
||||||
// methods
|
// methods
|
||||||
for (StructMethod mt : cl.getMethods()) {
|
for (StructMethod mt : cl.getMethods()) {
|
||||||
@ -232,15 +227,15 @@ public class ClassWriter {
|
|||||||
|
|
||||||
int position = buffer.length();
|
int position = buffer.length();
|
||||||
if (hasContent) {
|
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);
|
boolean methodSkipped = !methodToJava(node, mt, buffer, indent + 1, method_tracer);
|
||||||
if (!methodSkipped) {
|
if (!methodSkipped) {
|
||||||
hasContent = true;
|
hasContent = true;
|
||||||
DecompilerContext.getBytecodeSourceMapper().addTracer(cl.qualifiedName,
|
DecompilerContext.getBytecodeSourceMapper().addTracer(cl.qualifiedName,
|
||||||
InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()), method_tracer);
|
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 {
|
else {
|
||||||
buffer.setLength(position);
|
buffer.setLength(position);
|
||||||
@ -257,9 +252,9 @@ public class ClassWriter {
|
|||||||
if (hide) continue;
|
if (hide) continue;
|
||||||
|
|
||||||
if (hasContent) {
|
if (hasContent) {
|
||||||
buffer.append(lineSeparator);
|
buffer.appendLineSeparator();
|
||||||
}
|
}
|
||||||
classToJava(inner, buffer, indent + 1);
|
classToJava(inner, buffer, indent + 1, tracer);
|
||||||
|
|
||||||
hasContent = true;
|
hasContent = true;
|
||||||
}
|
}
|
||||||
@ -268,7 +263,7 @@ public class ClassWriter {
|
|||||||
buffer.appendIndent(indent).append('}');
|
buffer.appendIndent(indent).append('}');
|
||||||
|
|
||||||
if (node.type != ClassNode.CLASS_ANONYMOUS) {
|
if (node.type != ClassNode.CLASS_ANONYMOUS) {
|
||||||
buffer.append(lineSeparator);
|
buffer.appendLineSeparator();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
@ -794,7 +789,7 @@ public class ClassWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
buffer.append(';');
|
buffer.append(';');
|
||||||
buffer.append(lineSeparator);
|
buffer.appendLineSeparator();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!clinit && !dinit) {
|
if (!clinit && !dinit) {
|
||||||
@ -811,7 +806,7 @@ public class ClassWriter {
|
|||||||
|
|
||||||
if (root != null && !methodWrapper.decompiledWithErrors) { // check for existence
|
if (root != null && !methodWrapper.decompiledWithErrors) { // check for existence
|
||||||
try {
|
try {
|
||||||
tracer.incrementCurrentSourceLine(buffer.count(lineSeparator, start_index_method));
|
tracer.incrementCurrentSourceLine(buffer.countLines(start_index_method));
|
||||||
int startLine = tracer.getCurrentSourceLine();
|
int startLine = tracer.getCurrentSourceLine();
|
||||||
|
|
||||||
TextBuffer code = root.toJava(indent + 1, tracer);
|
TextBuffer code = root.toJava(indent + 1, tracer);
|
||||||
@ -845,31 +840,36 @@ public class ClassWriter {
|
|||||||
|
|
||||||
// save total lines
|
// save total lines
|
||||||
// TODO: optimize
|
// TODO: optimize
|
||||||
tracer.setCurrentSourceLine(buffer.count(lineSeparator, start_index_method));
|
tracer.setCurrentSourceLine(buffer.countLines(start_index_method));
|
||||||
|
|
||||||
return !hideMethod;
|
return !hideMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mapLines(TextBuffer code, StructLineNumberTableAttribute table, BytecodeMappingTracer tracer, int startLine) {
|
private void mapLines(TextBuffer code, StructLineNumberTableAttribute table, BytecodeMappingTracer tracer, int startLine) {
|
||||||
// build line start offsets map
|
// build line start offsets map
|
||||||
HashMap<Integer, Integer> lineStartOffsets = new HashMap<Integer, Integer>();
|
HashMap<Integer, Set<Integer>> lineStartOffsets = new HashMap<Integer, Set<Integer>>();
|
||||||
for (Map.Entry<Integer, Integer> entry : tracer.getMapping().entrySet()) {
|
for (Map.Entry<Integer, Integer> entry : tracer.getMapping().entrySet()) {
|
||||||
Integer lineNumber = entry.getValue() - startLine;
|
Integer lineNumber = entry.getValue() - startLine;
|
||||||
Integer curr = lineStartOffsets.get(lineNumber);
|
Set<Integer> curr = lineStartOffsets.get(lineNumber);
|
||||||
if (curr == null || curr > entry.getKey()) {
|
if (curr == null) {
|
||||||
lineStartOffsets.put(lineNumber, entry.getKey());
|
curr = new TreeSet<Integer>(); // requires natural sorting!
|
||||||
}
|
}
|
||||||
|
curr.add(entry.getKey());
|
||||||
|
lineStartOffsets.put(lineNumber, curr);
|
||||||
}
|
}
|
||||||
String lineSeparator = DecompilerContext.getNewLineSeparator();
|
String lineSeparator = DecompilerContext.getNewLineSeparator();
|
||||||
StringBuilder text = code.getOriginalText();
|
StringBuilder text = code.getOriginalText();
|
||||||
int pos = text.indexOf(lineSeparator);
|
int pos = text.indexOf(lineSeparator);
|
||||||
int lineNumber = 0;
|
int lineNumber = 0;
|
||||||
while (pos != -1) {
|
while (pos != -1) {
|
||||||
Integer startOffset = lineStartOffsets.get(lineNumber);
|
Set<Integer> startOffsets = lineStartOffsets.get(lineNumber);
|
||||||
if (startOffset != null) {
|
if (startOffsets != null) {
|
||||||
int number = table.findLineNumber(startOffset);
|
for (Integer offset : startOffsets) {
|
||||||
|
int number = table.findLineNumber(offset);
|
||||||
if (number >= 0) {
|
if (number >= 0) {
|
||||||
code.setLineMapping(number, pos);
|
code.setLineMapping(number, pos);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pos = text.indexOf(lineSeparator, pos+1);
|
pos = text.indexOf(lineSeparator, pos+1);
|
||||||
|
@ -256,7 +256,7 @@ public class ClassesProcessor {
|
|||||||
new NestedMemberAccess().propagateMemberAccess(root);
|
new NestedMemberAccess().propagateMemberAccess(root);
|
||||||
|
|
||||||
TextBuffer classBuffer = new TextBuffer(AVERAGE_CLASS_SIZE);
|
TextBuffer classBuffer = new TextBuffer(AVERAGE_CLASS_SIZE);
|
||||||
new ClassWriter().classToJava(root, classBuffer, 0);
|
new ClassWriter().classToJava(root, classBuffer, 0, null);
|
||||||
|
|
||||||
String lineSeparator = DecompilerContext.getNewLineSeparator();
|
String lineSeparator = DecompilerContext.getNewLineSeparator();
|
||||||
int total_offset_lines = 0;
|
int total_offset_lines = 0;
|
||||||
|
@ -115,7 +115,7 @@ public class TextBuffer {
|
|||||||
while (currentLine < srcLines.length) {
|
while (currentLine < srcLines.length) {
|
||||||
String line = srcLines[currentLine];
|
String line = srcLines[currentLine];
|
||||||
int lineEnd = currentLineStartOffset + line.length() + myLineSeparator.length();
|
int lineEnd = currentLineStartOffset + line.length() + myLineSeparator.length();
|
||||||
if (markOffset >= currentLineStartOffset && markOffset <= lineEnd) {
|
if (markOffset <= lineEnd) {
|
||||||
int requiredLine = markLine - 1;
|
int requiredLine = markLine - 1;
|
||||||
int linesToAdd = requiredLine - dumpedLines;
|
int linesToAdd = requiredLine - dumpedLines;
|
||||||
dumpedLines = requiredLine;
|
dumpedLines = requiredLine;
|
||||||
@ -223,6 +223,14 @@ public class TextBuffer {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int countLines() {
|
||||||
|
return countLines(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int countLines(int from) {
|
||||||
|
return count(myLineSeparator, from);
|
||||||
|
}
|
||||||
|
|
||||||
public int count(String substring, int from) {
|
public int count(String substring, int from) {
|
||||||
int count = 0, length = substring.length(), p = from;
|
int count = 0, length = substring.length(), p = from;
|
||||||
while ((p = myStringBuilder.indexOf(substring, p)) > 0) {
|
while ((p = myStringBuilder.indexOf(substring, p)) > 0) {
|
||||||
|
@ -270,9 +270,11 @@ public class NewExprent extends Exprent {
|
|||||||
}
|
}
|
||||||
Exprent methodObject = constructor == null ? null : constructor.getInstance();
|
Exprent methodObject = constructor == null ? null : constructor.getInstance();
|
||||||
new ClassWriter().classLambdaToJava(child, buf, methodObject, indent);
|
new ClassWriter().classLambdaToJava(child, buf, methodObject, indent);
|
||||||
|
tracer.incrementCurrentSourceLine(buf.countLines());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
new ClassWriter().classToJava(child, buf, indent);
|
new ClassWriter().classToJava(child, buf, indent, tracer);
|
||||||
|
tracer.incrementCurrentSourceLine(buf.countLines());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (directArrayInit) {
|
else if (directArrayInit) {
|
||||||
|
@ -17,7 +17,6 @@ package org.jetbrains.java.decompiler.modules.decompiler.exps;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.jetbrains.java.decompiler.code.CodeConstants;
|
import org.jetbrains.java.decompiler.code.CodeConstants;
|
||||||
import org.jetbrains.java.decompiler.main.ClassWriter;
|
import org.jetbrains.java.decompiler.main.ClassWriter;
|
||||||
@ -92,7 +91,7 @@ public class VarExprent extends Exprent {
|
|||||||
|
|
||||||
if (classdef) {
|
if (classdef) {
|
||||||
ClassNode child = DecompilerContext.getClassProcessor().getMapRootClasses().get(vartype.value);
|
ClassNode child = DecompilerContext.getClassProcessor().getMapRootClasses().get(vartype.value);
|
||||||
new ClassWriter().classToJava(child, buffer, indent);
|
new ClassWriter().classToJava(child, buffer, indent, tracer);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
String name = null;
|
String name = null;
|
||||||
|
BIN
testData/classes/pkg/TestClassSimpleBytecodeMapping$1.class
Normal file
BIN
testData/classes/pkg/TestClassSimpleBytecodeMapping$1.class
Normal file
Binary file not shown.
Binary file not shown.
@ -3,6 +3,11 @@ package pkg;
|
|||||||
public class TestClassSimpleBytecodeMapping {
|
public class TestClassSimpleBytecodeMapping {
|
||||||
public int test() {
|
public int test() {
|
||||||
System.out.println("before");
|
System.out.println("before");
|
||||||
|
this.run(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
System.out.println("Runnable");
|
||||||
|
}
|
||||||
|
});
|
||||||
if(Math.random() > 0.0D) {
|
if(Math.random() > 0.0D) {
|
||||||
System.out.println("0");
|
System.out.println("0");
|
||||||
return 0;
|
return 0;
|
||||||
@ -11,26 +16,42 @@ public class TestClassSimpleBytecodeMapping {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void run(Runnable var1) {
|
||||||
|
var1.run();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class pkg/TestClassSimpleBytecodeMapping{
|
class pkg/TestClassSimpleBytecodeMapping{
|
||||||
|
method run (Ljava/lang/Runnable;)V{
|
||||||
|
1 20
|
||||||
|
}
|
||||||
|
|
||||||
method test ()I{
|
method test ()I{
|
||||||
0 4
|
0 4
|
||||||
3 4
|
3 4
|
||||||
5 4
|
5 4
|
||||||
8 5
|
11 5
|
||||||
b 5
|
14 10
|
||||||
c 5
|
17 10
|
||||||
d 5
|
18 10
|
||||||
10 6
|
19 10
|
||||||
13 6
|
1c 11
|
||||||
15 6
|
1f 11
|
||||||
18 7
|
21 11
|
||||||
19 7
|
24 12
|
||||||
1a 9
|
25 12
|
||||||
1d 9
|
26 14
|
||||||
1f 9
|
29 14
|
||||||
22 10
|
2b 14
|
||||||
23 10
|
2e 15
|
||||||
|
2f 15
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class pkg/TestClassSimpleBytecodeMapping$1{
|
||||||
|
method run ()V{
|
||||||
|
0 7
|
||||||
|
3 7
|
||||||
|
5 7
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
package pkg;
|
package pkg;
|
||||||
|
|
||||||
|
import java.lang.Override;
|
||||||
|
import java.lang.Runnable;
|
||||||
|
|
||||||
public class TestClassSimpleBytecodeMapping {
|
public class TestClassSimpleBytecodeMapping {
|
||||||
|
|
||||||
public TestClassSimpleBytecodeMapping() {}
|
public TestClassSimpleBytecodeMapping() {}
|
||||||
@ -8,6 +11,13 @@ public class TestClassSimpleBytecodeMapping {
|
|||||||
|
|
||||||
System.out.println("before");
|
System.out.println("before");
|
||||||
|
|
||||||
|
run(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
System.out.println("Runnable");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if(Math.random() > 0) {
|
if(Math.random() > 0) {
|
||||||
System.out.println("0");
|
System.out.println("0");
|
||||||
return 0;
|
return 0;
|
||||||
@ -17,4 +27,7 @@ public class TestClassSimpleBytecodeMapping {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void run(Runnable r) {
|
||||||
|
r.run();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user