Fork of the Fernflower decompiler
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
fernflower/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java

204 lines
7.0 KiB

// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.java.decompiler.main.rels;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.code.InstructionSequence;
import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import org.jetbrains.java.decompiler.modules.code.DeadCodeHelper;
import org.jetbrains.java.decompiler.modules.decompiler.*;
import org.jetbrains.java.decompiler.modules.decompiler.deobfuscator.ExceptionDeobfuscator;
import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.struct.StructMethod;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import java.io.IOException;
public class MethodProcessorRunnable implements Runnable {
public final Object lock = new Object();
private final StructClass klass;
private final StructMethod method;
private final MethodDescriptor methodDescriptor;
private final VarProcessor varProc;
private final DecompilerContext parentContext;
private volatile RootStatement root;
private volatile Throwable error;
private volatile boolean finished = false;
public MethodProcessorRunnable(StructClass klass,
StructMethod method,
MethodDescriptor methodDescriptor,
VarProcessor varProc,
DecompilerContext parentContext) {
this.klass = klass;
this.method = method;
this.methodDescriptor = methodDescriptor;
this.varProc = varProc;
this.parentContext = parentContext;
}
@Override
public void run() {
error = null;
root = null;
try {
DecompilerContext.setCurrentContext(parentContext);
root = codeToJava(klass, method, methodDescriptor, varProc);
}
catch (Throwable t) {
error = t;
}
finally {
DecompilerContext.setCurrentContext(null);
}
finished = true;
synchronized (lock) {
lock.notifyAll();
}
}
public static RootStatement codeToJava(StructClass cl, StructMethod mt, MethodDescriptor md, VarProcessor varProc) throws IOException {
boolean isInitializer = CodeConstants.CLINIT_NAME.equals(mt.getName()); // for now static initializer only
mt.expandData(cl);
InstructionSequence seq = mt.getInstructionSequence();
ControlFlowGraph graph = new ControlFlowGraph(seq);
DeadCodeHelper.removeDeadBlocks(graph);
graph.inlineJsr(cl, mt);
// TODO: move to the start, before jsr inlining
DeadCodeHelper.connectDummyExitBlock(graph);
DeadCodeHelper.removeGotos(graph);
ExceptionDeobfuscator.removeCircularRanges(graph);
ExceptionDeobfuscator.restorePopRanges(graph);
if (DecompilerContext.getOption(IFernflowerPreferences.REMOVE_EMPTY_RANGES)) {
ExceptionDeobfuscator.removeEmptyRanges(graph);
}
if (DecompilerContext.getOption(IFernflowerPreferences.ENSURE_SYNCHRONIZED_MONITOR)) {
// special case: search for 'synchronized' ranges w/o monitorexit instruction (as generated by Kotlin and Scala)
DeadCodeHelper.extendSynchronizedRangeToMonitorexit(graph);
}
if (DecompilerContext.getOption(IFernflowerPreferences.NO_EXCEPTIONS_RETURN)) {
// special case: single return instruction outside of a protected range
DeadCodeHelper.incorporateValueReturns(graph);
}
// ExceptionDeobfuscator.restorePopRanges(graph);
ExceptionDeobfuscator.insertEmptyExceptionHandlerBlocks(graph);
DeadCodeHelper.mergeBasicBlocks(graph);
DecompilerContext.getCounterContainer().setCounter(CounterContainer.VAR_COUNTER, mt.getLocalVariables());
if (ExceptionDeobfuscator.hasObfuscatedExceptions(graph)) {
DecompilerContext.getLogger().writeMessage("Heavily obfuscated exception ranges found!", IFernflowerLogger.Severity.WARN);
if (!ExceptionDeobfuscator.handleMultipleEntryExceptionRanges(graph)) {
DecompilerContext.getLogger().writeMessage("Found multiple entry exception ranges which could not be splitted", IFernflowerLogger.Severity.WARN);
}
ExceptionDeobfuscator.insertDummyExceptionHandlerBlocks(graph, mt.getBytecodeVersion());
}
RootStatement root = DomHelper.parseGraph(graph);
FinallyProcessor fProc = new FinallyProcessor(md, varProc);
while (fProc.iterateGraph(cl, mt, root, graph)) {
root = DomHelper.parseGraph(graph);
}
// remove synchronized exception handler
// not until now because of comparison between synchronized statements in the finally cycle
DomHelper.removeSynchronizedHandler(root);
// LabelHelper.lowContinueLabels(root, new HashSet<StatEdge>());
SequenceHelper.condenseSequences(root);
ClearStructHelper.clearStatements(root);
ExprProcessor proc = new ExprProcessor(md, varProc);
proc.processStatement(root, mt, cl);
SequenceHelper.condenseSequences(root);
StackVarsProcessor stackProc = new StackVarsProcessor();
do {
stackProc.simplifyStackVars(root, mt, cl);
varProc.setVarVersions(root);
}
while (new PPandMMHelper().findPPandMM(root));
while (true) {
LabelHelper.cleanUpEdges(root);
do {
MergeHelper.enhanceLoops(root);
}
while (LoopExtractHelper.extractLoops(root) || IfHelper.mergeAllIfs(root));
if (DecompilerContext.getOption(IFernflowerPreferences.IDEA_NOT_NULL_ANNOTATION)) {
if (IdeaNotNullHelper.removeHardcodedChecks(root, mt)) {
SequenceHelper.condenseSequences(root);
stackProc.simplifyStackVars(root, mt, cl);
varProc.setVarVersions(root);
}
}
LabelHelper.identifyLabels(root);
if (InlineSingleBlockHelper.inlineSingleBlocks(root)) {
continue;
}
// initializer may have at most one return point, so no transformation of method exits permitted
if (isInitializer || !ExitHelper.condenseExits(root)) {
break;
}
// FIXME: !!
//if(!EliminateLoopsHelper.eliminateLoops(root)) {
// break;
//}
}
ExitHelper.removeRedundantReturns(root);
SecondaryFunctionsHelper.identifySecondaryFunctions(root, varProc);
varProc.setVarDefinitions(root);
// must be the last invocation, because it makes the statement structure inconsistent
// FIXME: new edge type needed
LabelHelper.replaceContinueWithBreak(root);
mt.releaseResources();
return root;
}
public RootStatement getResult() throws Throwable {
Throwable t = error;
if (t != null) throw t;
return root;
}
public boolean isFinished() {
return finished;
}
}