| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.jetbrains.java.decompiler.main.rels; |
| |
| 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 java.io.IOException; |
| |
| public class MethodProcessorThread implements Runnable { |
| |
| public final Object lock = new Object(); |
| |
| private final StructMethod method; |
| private final VarProcessor varproc; |
| private final DecompilerContext parentContext; |
| |
| private volatile RootStatement root; |
| private volatile Throwable error; |
| |
| public MethodProcessorThread(StructMethod method, VarProcessor varproc, DecompilerContext parentContext) { |
| this.method = method; |
| this.varproc = varproc; |
| this.parentContext = parentContext; |
| } |
| |
| public void run() { |
| |
| DecompilerContext.setCurrentContext(parentContext); |
| |
| error = null; |
| root = null; |
| |
| try { |
| root = codeToJava(method, varproc); |
| |
| synchronized (lock) { |
| lock.notifyAll(); |
| } |
| } |
| catch (ThreadDeath ex) { |
| throw ex; |
| } |
| catch (Throwable ex) { |
| error = ex; |
| } |
| } |
| |
| public static RootStatement codeToJava(StructMethod mt, VarProcessor varproc) throws IOException { |
| |
| StructClass cl = mt.getClassStruct(); |
| |
| boolean isInitializer = "<clinit>".equals(mt.getName()); // for now static initializer only |
| |
| mt.expandData(); |
| InstructionSequence seq = mt.getInstructionSequence(); |
| ControlFlowGraph graph = new ControlFlowGraph(seq); |
| |
| // System.out.println(graph.toString()); |
| |
| |
| // if(mt.getName().endsWith("_getActiveServers")) { |
| // System.out.println(); |
| // } |
| |
| //DotExporter.toDotFile(graph, new File("c:\\Temp\\fern1.dot"), true); |
| |
| DeadCodeHelper.removeDeadBlocks(graph); |
| graph.inlineJsr(mt); |
| |
| // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern4.dot"), true); |
| |
| // TODO: move to the start, before jsr inlining |
| DeadCodeHelper.connectDummyExitBlock(graph); |
| |
| DeadCodeHelper.removeGotos(graph); |
| ExceptionDeobfuscator.removeCircularRanges(graph); |
| //DeadCodeHelper.removeCircularRanges(graph); |
| |
| |
| // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true); |
| |
| ExceptionDeobfuscator.restorePopRanges(graph); |
| |
| if (DecompilerContext.getOption(IFernflowerPreferences.REMOVE_EMPTY_RANGES)) { |
| ExceptionDeobfuscator.removeEmptyRanges(graph); |
| } |
| |
| // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true); |
| |
| if (DecompilerContext.getOption(IFernflowerPreferences.NO_EXCEPTIONS_RETURN)) { |
| // special case: single return instruction outside of a protected range |
| DeadCodeHelper.incorporateValueReturns(graph); |
| } |
| |
| // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true); |
| |
| // ExceptionDeobfuscator.restorePopRanges(graph); |
| ExceptionDeobfuscator.insertEmptyExceptionHandlerBlocks(graph); |
| |
| DeadCodeHelper.mergeBasicBlocks(graph); |
| |
| DecompilerContext.getCounterContainer().setCounter(CounterContainer.VAR_COUNTER, mt.getLocalVariables()); |
| |
| //DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true); |
| //System.out.println(graph.toString()); |
| |
| if (ExceptionDeobfuscator.hasObfuscatedExceptions(graph)) { |
| DecompilerContext.getLogger().writeMessage("Heavily obfuscated exception ranges found!", IFernflowerLogger.Severity.WARN); |
| } |
| |
| RootStatement root = DomHelper.parseGraph(graph); |
| |
| FinallyProcessor fproc = new FinallyProcessor(varproc); |
| while (fproc.iterateGraph(mt, root, graph)) { |
| |
| //DotExporter.toDotFile(graph, new File("c:\\Temp\\fern2.dot"), true); |
| //System.out.println(graph.toString()); |
| //System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); |
| |
| root = DomHelper.parseGraph(graph); |
| } |
| |
| // remove synchronized exception handler |
| // not until now because of comparison between synchronized statements in the finally cycle |
| DomHelper.removeSynchronizedHandler(root); |
| |
| // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true); |
| // System.out.println(graph.toString()); |
| |
| // LabelHelper.lowContinueLabels(root, new HashSet<StatEdge>()); |
| |
| SequenceHelper.condenseSequences(root); |
| |
| ClearStructHelper.clearStatements(root); |
| |
| ExprProcessor proc = new ExprProcessor(); |
| proc.processStatement(root, cl); |
| |
| // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true); |
| // System.out.println(graph.toString()); |
| |
| //System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); |
| |
| while (true) { |
| StackVarsProcessor stackproc = new StackVarsProcessor(); |
| stackproc.simplifyStackVars(root, mt, cl); |
| |
| //System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); |
| |
| varproc.setVarVersions(root); |
| |
| // System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); |
| |
| if (!new PPandMMHelper().findPPandMM(root)) { |
| break; |
| } |
| } |
| |
| while (true) { |
| |
| LabelHelper.cleanUpEdges(root); |
| |
| while (true) { |
| |
| MergeHelper.enhanceLoops(root); |
| |
| if (LoopExtractHelper.extractLoops(root)) { |
| continue; |
| } |
| |
| if (!IfHelper.mergeAllIfs(root)) { |
| break; |
| } |
| } |
| |
| if (DecompilerContext.getOption(IFernflowerPreferences.IDEA_NOT_NULL_ANNOTATION)) { |
| |
| if (IdeaNotNullHelper.removeHardcodedChecks(root, mt)) { |
| |
| SequenceHelper.condenseSequences(root); |
| |
| StackVarsProcessor stackproc = new StackVarsProcessor(); |
| stackproc.simplifyStackVars(root, mt, cl); |
| |
| varproc.setVarVersions(root); |
| } |
| } |
| |
| LabelHelper.identifyLabels(root); |
| |
| // System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); |
| |
| 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.setVarDefinitions(root); |
| |
| // must be the last invocation, because it makes the statement structure inconsistent |
| // FIXME: new edge type needed |
| LabelHelper.replaceContinueWithBreak(root); |
| |
| mt.releaseResources(); |
| |
| // System.out.println("++++++++++++++++++++++/// \r\n"+root.toJava()); |
| |
| return root; |
| } |
| |
| public RootStatement getResult() throws Throwable { |
| Throwable t = error; |
| if (t != null) throw t; |
| return root; |
| } |
| |
| public Throwable getError() { |
| return error; |
| } |
| } |