Decompilation of synchronized blocks generated by the Kotlin compiler

master
upnotes 6 years ago committed by Roman Shevchenko
parent 2431c0fe94
commit 7e98f686c0
  1. 2
      src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java
  2. 5
      src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java
  3. 147
      src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java
  4. 1
      test/org/jetbrains/java/decompiler/SingleClassesTest.java
  5. BIN
      testData/classes/pkg/TestSynchronizedUnprotected.class
  6. 25
      testData/results/TestSynchronizedUnprotected.dec

@ -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

@ -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
Loading…
Cancel
Save