Decompilation of synchronized blocks generated by the Kotlin compiler
This commit is contained in:
committed by
Roman Shevchenko
parent
2431c0fe94
commit
7e98f686c0
@@ -17,6 +17,7 @@ public interface IFernflowerPreferences {
|
||||
String HIDE_DEFAULT_CONSTRUCTOR = "hdc";
|
||||
String DECOMPILE_GENERIC_SIGNATURES = "dgs";
|
||||
String NO_EXCEPTIONS_RETURN = "ner";
|
||||
String KT_SYNCHRONIZED_MONITOR = "ksm";
|
||||
String DECOMPILE_ENUM = "den";
|
||||
String REMOVE_GET_CLASS_NEW = "rgn";
|
||||
String LITERALS_AS_IS = "lit";
|
||||
@@ -61,6 +62,7 @@ public interface IFernflowerPreferences {
|
||||
defaults.put(HIDE_DEFAULT_CONSTRUCTOR, "1");
|
||||
defaults.put(DECOMPILE_GENERIC_SIGNATURES, "0");
|
||||
defaults.put(NO_EXCEPTIONS_RETURN, "1");
|
||||
defaults.put(KT_SYNCHRONIZED_MONITOR, "1");
|
||||
defaults.put(DECOMPILE_ENUM, "1");
|
||||
defaults.put(REMOVE_GET_CLASS_NEW, "1");
|
||||
defaults.put(LITERALS_AS_IS, "0");
|
||||
|
||||
@@ -88,6 +88,11 @@ public class MethodProcessorRunnable implements Runnable {
|
||||
ExceptionDeobfuscator.removeEmptyRanges(graph);
|
||||
}
|
||||
|
||||
if (DecompilerContext.getOption(IFernflowerPreferences.KT_SYNCHRONIZED_MONITOR)) {
|
||||
// special case: search for 'synchronized' ranges w/o monitorexit instruction (generated by the Kotlin compiler)
|
||||
DeadCodeHelper.extendSynchronizedRangeToMonitorexit(graph);
|
||||
}
|
||||
|
||||
if (DecompilerContext.getOption(IFernflowerPreferences.NO_EXCEPTIONS_RETURN)) {
|
||||
// special case: single return instruction outside of a protected range
|
||||
DeadCodeHelper.incorporateValueReturns(graph);
|
||||
|
||||
@@ -4,6 +4,7 @@ package org.jetbrains.java.decompiler.modules.code;
|
||||
import org.jetbrains.java.decompiler.code.CodeConstants;
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
import org.jetbrains.java.decompiler.code.InstructionSequence;
|
||||
import org.jetbrains.java.decompiler.code.SimpleInstructionSequence;
|
||||
import org.jetbrains.java.decompiler.code.cfg.BasicBlock;
|
||||
import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph;
|
||||
import org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG;
|
||||
@@ -254,6 +255,152 @@ public class DeadCodeHelper {
|
||||
}
|
||||
}
|
||||
|
||||
public static void extendSynchronizedRangeToMonitorexit(ControlFlowGraph graph) {
|
||||
|
||||
while(true) {
|
||||
|
||||
boolean range_extended = false;
|
||||
|
||||
for (ExceptionRangeCFG range : graph.getExceptions()) {
|
||||
|
||||
Set<BasicBlock> setPreds = new HashSet<>();
|
||||
for (BasicBlock block : range.getProtectedRange()) {
|
||||
setPreds.addAll(block.getPreds());
|
||||
}
|
||||
setPreds.removeAll(range.getProtectedRange());
|
||||
|
||||
if(setPreds.size() != 1) {
|
||||
continue; // multiple predecessors, obfuscated range
|
||||
}
|
||||
|
||||
BasicBlock predBlock = setPreds.iterator().next();
|
||||
InstructionSequence predSeq = predBlock.getSeq();
|
||||
if(predSeq.isEmpty() || predSeq.getLastInstr().opcode != CodeConstants.opc_monitorenter) {
|
||||
continue; // not a synchronized range
|
||||
}
|
||||
|
||||
boolean monitorexit_in_range = false;
|
||||
Set<BasicBlock> setProtectedBlocks = new HashSet<>();
|
||||
setProtectedBlocks.addAll(range.getProtectedRange());
|
||||
setProtectedBlocks.add(range.getHandler());
|
||||
|
||||
for (BasicBlock block : setProtectedBlocks) {
|
||||
InstructionSequence blockSeq = block.getSeq();
|
||||
for (int i = 0; i < blockSeq.length(); i++) {
|
||||
if (blockSeq.getInstr(i).opcode == CodeConstants.opc_monitorexit) {
|
||||
monitorexit_in_range = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(monitorexit_in_range) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(monitorexit_in_range) {
|
||||
continue; // protected range already contains monitorexit
|
||||
}
|
||||
|
||||
Set<BasicBlock> setSuccs = new HashSet<>();
|
||||
for (BasicBlock block : range.getProtectedRange()) {
|
||||
setSuccs.addAll(block.getSuccs());
|
||||
}
|
||||
setSuccs.removeAll(range.getProtectedRange());
|
||||
|
||||
if(setSuccs.size() != 1) {
|
||||
continue; // non-unique successor
|
||||
}
|
||||
|
||||
BasicBlock succBlock = setSuccs.iterator().next();
|
||||
InstructionSequence succSeq = succBlock.getSeq();
|
||||
|
||||
int succ_monitorexit_index = -1;
|
||||
for (int i = 0; i < succSeq.length(); i++) {
|
||||
if (succSeq.getInstr(i).opcode == CodeConstants.opc_monitorexit) {
|
||||
succ_monitorexit_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(succ_monitorexit_index < 0) {
|
||||
continue; // monitorexit not found in the single successor block
|
||||
}
|
||||
|
||||
BasicBlock handlerBlock = range.getHandler();
|
||||
if(handlerBlock.getSuccs().size() != 1) {
|
||||
continue; // non-unique handler successor
|
||||
}
|
||||
BasicBlock succHandler = handlerBlock.getSuccs().get(0);
|
||||
InstructionSequence succHandlerSeq = succHandler.getSeq();
|
||||
if(succHandlerSeq.isEmpty() || succHandlerSeq.getLastInstr().opcode != CodeConstants.opc_athrow) {
|
||||
continue; // not a standard synchronized range
|
||||
}
|
||||
|
||||
int handler_monitorexit_index = -1;
|
||||
for (int i = 0; i < succHandlerSeq.length(); i++) {
|
||||
if (succHandlerSeq.getInstr(i).opcode == CodeConstants.opc_monitorexit) {
|
||||
handler_monitorexit_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(handler_monitorexit_index < 0) {
|
||||
continue; // monitorexit not found in the handler successor block
|
||||
}
|
||||
|
||||
// checks successful, prerequisites satisfied, now extend the range
|
||||
if(succ_monitorexit_index < succSeq.length() - 1) { // split block
|
||||
|
||||
SimpleInstructionSequence seq = new SimpleInstructionSequence();
|
||||
for(int counter = 0; counter < succ_monitorexit_index; counter++) {
|
||||
seq.addInstruction(succSeq.getInstr(0), -1);
|
||||
succSeq.removeInstruction(0);
|
||||
}
|
||||
|
||||
// build a separate block
|
||||
BasicBlock newblock = new BasicBlock(++graph.last_id);
|
||||
newblock.setSeq(seq);
|
||||
|
||||
// insert new block
|
||||
for (BasicBlock block : succBlock.getPreds()) {
|
||||
block.replaceSuccessor(succBlock, newblock);
|
||||
}
|
||||
|
||||
newblock.addSuccessor(succBlock);
|
||||
graph.getBlocks().addWithKey(newblock, newblock.id);
|
||||
|
||||
succBlock = newblock;
|
||||
}
|
||||
|
||||
// copy exception edges and extend protected ranges (successor block)
|
||||
BasicBlock rangeExitBlock = succBlock.getPreds().get(0);
|
||||
for (int j = 0; j < rangeExitBlock.getSuccExceptions().size(); j++) {
|
||||
BasicBlock hd = rangeExitBlock.getSuccExceptions().get(j);
|
||||
succBlock.addSuccessorException(hd);
|
||||
|
||||
ExceptionRangeCFG rng = graph.getExceptionRange(hd, rangeExitBlock);
|
||||
rng.getProtectedRange().add(succBlock);
|
||||
}
|
||||
|
||||
// copy instructions (handler successor block)
|
||||
InstructionSequence handlerSeq = handlerBlock.getSeq();
|
||||
for(int counter = 0; counter < handler_monitorexit_index; counter++) {
|
||||
handlerSeq.addInstruction(succHandlerSeq.getInstr(0), -1);
|
||||
succHandlerSeq.removeInstruction(0);
|
||||
}
|
||||
|
||||
range_extended = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!range_extended) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static void incorporateValueReturns(ControlFlowGraph graph) {
|
||||
|
||||
for (BasicBlock block : graph.getBlocks()) {
|
||||
|
||||
@@ -109,6 +109,7 @@ public class SingleClassesTest {
|
||||
@Test public void testMissingConstructorCallBad() { doTest("pkg/TestMissingConstructorCallBad"); }
|
||||
@Test public void testEmptyBlocks() { doTest("pkg/TestEmptyBlocks"); }
|
||||
@Test public void testPrivateEmptyConstructor() { doTest("pkg/TestPrivateEmptyConstructor"); }
|
||||
@Test public void testSynchronizedUnprotected() { doTest("pkg/TestSynchronizedUnprotected"); }
|
||||
|
||||
|
||||
// TODO: fix all below
|
||||
|
||||
BIN
testData/classes/pkg/TestSynchronizedUnprotected.class
Normal file
BIN
testData/classes/pkg/TestSynchronizedUnprotected.class
Normal file
Binary file not shown.
25
testData/results/TestSynchronizedUnprotected.dec
Normal file
25
testData/results/TestSynchronizedUnprotected.dec
Normal file
@@ -0,0 +1,25 @@
|
||||
public final class TestSynchronizedUnprotected {
|
||||
public final void test() {
|
||||
synchronized(this) {// 5
|
||||
System.out.println("test");// 6
|
||||
}
|
||||
}// 7
|
||||
}
|
||||
|
||||
class 'TestSynchronizedUnprotected' {
|
||||
method 'test ()V' {
|
||||
3 2
|
||||
4 3
|
||||
7 3
|
||||
9 3
|
||||
e 5
|
||||
}
|
||||
}
|
||||
|
||||
Lines mapping:
|
||||
5 <-> 3
|
||||
6 <-> 4
|
||||
7 <-> 6
|
||||
Not mapped:
|
||||
8
|
||||
|
||||
Reference in New Issue
Block a user