summaryrefslogtreecommitdiff
path: root/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules')
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java436
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java40
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java209
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/DecHelper.java218
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java707
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/EliminateLoopsHelper.java214
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java345
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java913
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ExprentStack.java45
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java1087
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/IdeaNotNullHelper.java336
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java750
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java220
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java506
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java206
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/LowBreakHelper.java208
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java421
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java154
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/PrimitiveExprsList.java49
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java432
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java327
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java853
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/StackVarsProcessor.java735
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/StatEdge.java104
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java207
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorEngine.java128
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorTreeExceptionFilter.java184
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/FastExtendedPostdominanceHelper.java355
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/GenericDominatorEngine.java152
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraph.java26
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraphNode.java23
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java330
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/IrreducibleCFGDeobfuscator.java245
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AnnotationExprent.java114
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ArrayExprent.java126
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssertExprent.java51
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssignmentExprent.java204
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java402
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java151
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java133
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java200
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java615
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/IfExprent.java145
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java515
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/MonitorExprent.java83
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java504
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/SwitchExprent.java114
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java182
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectGraph.java136
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectNode.java67
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java574
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAConstructorSparseEx.java529
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAUConstructorSparseEx.java844
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java96
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java233
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java210
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java222
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/GeneralStatement.java74
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java416
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/RootStatement.java48
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java144
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java863
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java367
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SynchronizedStatement.java153
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/CheckTypesResult.java57
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java355
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java129
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java278
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionEdge.java56
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionNode.java80
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionPaar.java63
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsGraph.java167
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java334
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/renamer/ClassWrapperNode.java55
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/renamer/ConverterHelper.java143
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java463
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/renamer/PoolInterceptor.java50
77 files changed, 21910 insertions, 0 deletions
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java
new file mode 100644
index 000000000000..c7dd8ca1c173
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java
@@ -0,0 +1,436 @@
+/*
+ * 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.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.cfg.BasicBlock;
+import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph;
+import org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG;
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
+
+import java.util.*;
+
+public class DeadCodeHelper {
+
+ public static void removeDeadBlocks(ControlFlowGraph graph) {
+
+ LinkedList<BasicBlock> stack = new LinkedList<BasicBlock>();
+ HashSet<BasicBlock> setStacked = new HashSet<BasicBlock>();
+
+ stack.add(graph.getFirst());
+ setStacked.add(graph.getFirst());
+
+ while (!stack.isEmpty()) {
+ BasicBlock block = stack.removeFirst();
+
+ List<BasicBlock> lstSuccs = new ArrayList<BasicBlock>(block.getSuccs());
+ lstSuccs.addAll(block.getSuccExceptions());
+
+ for (BasicBlock succ : lstSuccs) {
+ if (!setStacked.contains(succ)) {
+ stack.add(succ);
+ setStacked.add(succ);
+ }
+ }
+ }
+
+ HashSet<BasicBlock> setAllBlocks = new HashSet<BasicBlock>(graph.getBlocks());
+ setAllBlocks.removeAll(setStacked);
+
+ for (BasicBlock block : setAllBlocks) {
+ graph.removeBlock(block);
+ }
+ }
+
+ public static void removeEmptyBlocks(ControlFlowGraph graph) {
+
+ List<BasicBlock> blocks = graph.getBlocks();
+
+ boolean cont;
+ do {
+ cont = false;
+
+ for (int i = blocks.size() - 1; i >= 0; i--) {
+ BasicBlock block = blocks.get(i);
+
+ if (removeEmptyBlock(graph, block, false)) {
+ cont = true;
+ break;
+ }
+ }
+ }
+ while (cont);
+ }
+
+ private static boolean removeEmptyBlock(ControlFlowGraph graph, BasicBlock block, boolean merging) {
+
+ boolean deletedRanges = false;
+
+ if (block.getSeq().isEmpty()) {
+
+ if (block.getSuccs().size() > 1) {
+ if (block.getPreds().size() > 1) {
+ // ambiguous block
+ throw new RuntimeException("ERROR: empty block with multiple predecessors and successors found");
+ }
+ else if (!merging) {
+ throw new RuntimeException("ERROR: empty block with multiple successors found");
+ }
+ }
+
+ HashSet<BasicBlock> setExits = new HashSet<BasicBlock>(graph.getLast().getPreds());
+
+ if (block.getPredExceptions().isEmpty() &&
+ (!setExits.contains(block) || block.getPreds().size() == 1)) {
+
+ if (setExits.contains(block)) {
+ BasicBlock pred = block.getPreds().get(0);
+
+ // FIXME: flag in the basic block
+ if (pred.getSuccs().size() != 1 || (!pred.getSeq().isEmpty()
+ && pred.getSeq().getLastInstr().group == CodeConstants.GROUP_SWITCH)) {
+ return false;
+ }
+ }
+
+ HashSet<BasicBlock> setPreds = new HashSet<BasicBlock>(block.getPreds());
+ HashSet<BasicBlock> setSuccs = new HashSet<BasicBlock>(block.getSuccs());
+
+ // collect common exception ranges of predecessors and successors
+ HashSet<BasicBlock> setCommonExceptionHandlers = null;
+ for (int i = 0; i < 2; ++i) {
+ for (BasicBlock pred : i == 0 ? setPreds : setSuccs) {
+ if (setCommonExceptionHandlers == null) {
+ setCommonExceptionHandlers = new HashSet<BasicBlock>(pred.getSuccExceptions());
+ }
+ else {
+ setCommonExceptionHandlers.retainAll(pred.getSuccExceptions());
+ }
+ }
+ }
+
+ // check the block to be in each of the common ranges
+ if (setCommonExceptionHandlers != null && !setCommonExceptionHandlers.isEmpty()) {
+ for (BasicBlock handler : setCommonExceptionHandlers) {
+ if (!block.getSuccExceptions().contains(handler)) {
+ return false;
+ }
+ }
+ }
+
+ // remove ranges consisting of this one block
+ List<ExceptionRangeCFG> lstRanges = graph.getExceptions();
+ for (int i = lstRanges.size() - 1; i >= 0; i--) {
+ ExceptionRangeCFG range = lstRanges.get(i);
+ List<BasicBlock> lst = range.getProtectedRange();
+
+ if (lst.size() == 1 && lst.get(0) == block) {
+ if (DecompilerContext.getOption(IFernflowerPreferences.REMOVE_EMPTY_RANGES)) {
+ block.removeSuccessorException(range.getHandler());
+ lstRanges.remove(i);
+
+ deletedRanges = true;
+ }
+ else {
+ return false;
+ }
+ }
+ }
+
+
+ // connect remaining nodes
+ if (merging) {
+ BasicBlock pred = block.getPreds().get(0);
+ pred.removeSuccessor(block);
+
+ List<BasicBlock> lstSuccs = new ArrayList<BasicBlock>(block.getSuccs());
+ for (BasicBlock succ : lstSuccs) {
+ block.removeSuccessor(succ);
+ pred.addSuccessor(succ);
+ }
+ }
+ else {
+ for (BasicBlock pred : setPreds) {
+ for (BasicBlock succ : setSuccs) {
+ pred.replaceSuccessor(block, succ);
+ }
+ }
+ }
+
+ // finally exit edges
+ Set<BasicBlock> setFinallyExits = graph.getFinallyExits();
+ if (setFinallyExits.contains(block)) {
+ setFinallyExits.remove(block);
+ setFinallyExits.add(setPreds.iterator().next());
+ }
+
+ // replace first if necessary
+ if (graph.getFirst() == block) {
+ if (setSuccs.size() != 1) {
+ throw new RuntimeException("multiple or no entry blocks!");
+ }
+ else {
+ graph.setFirst(setSuccs.iterator().next());
+ }
+ }
+
+ // remove this block
+ graph.removeBlock(block);
+
+ if (deletedRanges) {
+ removeDeadBlocks(graph);
+ }
+ }
+ }
+
+ return deletedRanges;
+ }
+
+
+ public static boolean isDominator(ControlFlowGraph graph, BasicBlock block, BasicBlock dom) {
+
+ HashSet<BasicBlock> marked = new HashSet<BasicBlock>();
+
+ if (block == dom) {
+ return true;
+ }
+
+ LinkedList<BasicBlock> lstNodes = new LinkedList<BasicBlock>();
+ lstNodes.add(block);
+
+ while (!lstNodes.isEmpty()) {
+
+ BasicBlock node = lstNodes.remove(0);
+ if (marked.contains(node)) {
+ continue;
+ }
+ else {
+ marked.add(node);
+ }
+
+ if (node == graph.getFirst()) {
+ return false;
+ }
+
+ for (int i = 0; i < node.getPreds().size(); i++) {
+ BasicBlock pred = node.getPreds().get(i);
+ if (!marked.contains(pred) && pred != dom) {
+ lstNodes.add(pred);
+ }
+ }
+
+ for (int i = 0; i < node.getPredExceptions().size(); i++) {
+ BasicBlock pred = node.getPredExceptions().get(i);
+ if (!marked.contains(pred) && pred != dom) {
+ lstNodes.add(pred);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public static void removeGotos(ControlFlowGraph graph) {
+
+ for (BasicBlock block : graph.getBlocks()) {
+ Instruction instr = block.getLastInstruction();
+
+ if (instr != null && instr.opcode == CodeConstants.opc_goto) {
+ block.getSeq().removeInstruction(block.getSeq().length() - 1);
+ }
+ }
+
+ removeEmptyBlocks(graph);
+ }
+
+ public static void connectDummyExitBlock(ControlFlowGraph graph) {
+
+ BasicBlock exit = graph.getLast();
+ for (BasicBlock block : new HashSet<BasicBlock>(exit.getPreds())) {
+ exit.removePredecessor(block);
+ block.addSuccessor(exit);
+ }
+ }
+
+ public static void incorporateValueReturns(ControlFlowGraph graph) {
+
+ for (BasicBlock block : graph.getBlocks()) {
+ InstructionSequence seq = block.getSeq();
+
+ int len = seq.length();
+ if (len > 0 && len < 3) {
+
+ boolean ok = false;
+
+ if (seq.getLastInstr().opcode >= CodeConstants.opc_ireturn && seq.getLastInstr().opcode <= CodeConstants.opc_return) {
+ if (len == 1) {
+ ok = true;
+ }
+ else if (seq.getLastInstr().opcode != CodeConstants.opc_return) {
+ switch (seq.getInstr(0).opcode) {
+ case CodeConstants.opc_iload:
+ case CodeConstants.opc_lload:
+ case CodeConstants.opc_fload:
+ case CodeConstants.opc_dload:
+ case CodeConstants.opc_aload:
+ case CodeConstants.opc_aconst_null:
+ case CodeConstants.opc_bipush:
+ case CodeConstants.opc_sipush:
+ case CodeConstants.opc_lconst_0:
+ case CodeConstants.opc_lconst_1:
+ case CodeConstants.opc_fconst_0:
+ case CodeConstants.opc_fconst_1:
+ case CodeConstants.opc_fconst_2:
+ case CodeConstants.opc_dconst_0:
+ case CodeConstants.opc_dconst_1:
+ case CodeConstants.opc_ldc:
+ case CodeConstants.opc_ldc_w:
+ case CodeConstants.opc_ldc2_w:
+ ok = true;
+ }
+ }
+ }
+
+ if (ok) {
+
+ if (!block.getPreds().isEmpty()) {
+
+ HashSet<BasicBlock> setPredHandlersUnion = new HashSet<BasicBlock>();
+ HashSet<BasicBlock> setPredHandlersIntersection = new HashSet<BasicBlock>();
+
+ boolean firstpred = true;
+ for (BasicBlock pred : block.getPreds()) {
+ if (firstpred) {
+ setPredHandlersIntersection.addAll(pred.getSuccExceptions());
+ firstpred = false;
+ }
+ else {
+ setPredHandlersIntersection.retainAll(pred.getSuccExceptions());
+ }
+
+ setPredHandlersUnion.addAll(pred.getSuccExceptions());
+ }
+
+ // add exception ranges from predecessors
+ setPredHandlersIntersection.removeAll(block.getSuccExceptions());
+ BasicBlock predecessor = block.getPreds().get(0);
+
+ for (BasicBlock handler : setPredHandlersIntersection) {
+ ExceptionRangeCFG range = graph.getExceptionRange(handler, predecessor);
+
+ range.getProtectedRange().add(block);
+ block.addSuccessorException(handler);
+ }
+
+ // remove redundant ranges
+ HashSet<BasicBlock> setRangesToBeRemoved = new HashSet<BasicBlock>(block.getSuccExceptions());
+ setRangesToBeRemoved.removeAll(setPredHandlersUnion);
+
+ for (BasicBlock handler : setRangesToBeRemoved) {
+ ExceptionRangeCFG range = graph.getExceptionRange(handler, block);
+
+ if (range.getProtectedRange().size() > 1) {
+ range.getProtectedRange().remove(block);
+ block.removeSuccessorException(handler);
+ }
+ }
+ }
+
+
+ if (block.getPreds().size() == 1 && block.getPredExceptions().isEmpty()) {
+
+ BasicBlock bpred = block.getPreds().get(0);
+ if (bpred.getSuccs().size() == 1) {
+
+ // add exception ranges of predecessor
+ for (BasicBlock succ : bpred.getSuccExceptions()) {
+ if (!block.getSuccExceptions().contains(succ)) {
+ ExceptionRangeCFG range = graph.getExceptionRange(succ, bpred);
+
+ range.getProtectedRange().add(block);
+ block.addSuccessorException(succ);
+ }
+ }
+
+ // remove superfluous ranges from successors
+ for (BasicBlock succ : new HashSet<BasicBlock>(block.getSuccExceptions())) {
+ if (!bpred.getSuccExceptions().contains(succ)) {
+ ExceptionRangeCFG range = graph.getExceptionRange(succ, block);
+
+ if (range.getProtectedRange().size() > 1) {
+ range.getProtectedRange().remove(block);
+ block.removeSuccessorException(succ);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ public static void mergeBasicBlocks(ControlFlowGraph graph) {
+
+ while (true) {
+
+ boolean merged = false;
+
+ for (BasicBlock block : graph.getBlocks()) {
+
+ InstructionSequence seq = block.getSeq();
+
+ if (block.getSuccs().size() == 1) {
+ BasicBlock next = block.getSuccs().get(0);
+
+ if (next != graph.getLast() && (seq.isEmpty() || seq.getLastInstr().group != CodeConstants.GROUP_SWITCH)) {
+
+ if (next.getPreds().size() == 1 && next.getPredExceptions().isEmpty()
+ && next != graph.getFirst()) {
+ // TODO: implement a dummy start block
+ boolean sameRanges = true;
+ for (ExceptionRangeCFG range : graph.getExceptions()) {
+ if (range.getProtectedRange().contains(block) ^
+ range.getProtectedRange().contains(next)) {
+ sameRanges = false;
+ break;
+ }
+ }
+
+ if (sameRanges) {
+ seq.addSequence(next.getSeq());
+ next.getSeq().clear();
+
+ removeEmptyBlock(graph, next, true);
+
+ merged = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (!merged) {
+ break;
+ }
+ }
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java
new file mode 100644
index 000000000000..c6495e6ad13f
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java
@@ -0,0 +1,40 @@
+/*
+ * 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.modules.decompiler;
+
+import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
+
+import java.util.LinkedList;
+
+
+public class ClearStructHelper {
+
+ public static void clearStatements(RootStatement root) {
+
+ LinkedList<Statement> stack = new LinkedList<Statement>();
+ stack.add(root);
+
+ while (!stack.isEmpty()) {
+
+ Statement stat = stack.removeFirst();
+
+ stat.clearTempInformation();
+
+ stack.addAll(stat.getStats());
+ }
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java
new file mode 100644
index 000000000000..1b641a695f2e
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java
@@ -0,0 +1,209 @@
+/*
+ * 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.modules.decompiler;
+
+import org.jetbrains.java.decompiler.code.CodeConstants;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
+import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ConcatenationHelper {
+
+ private static final String builderClass = "java/lang/StringBuilder";
+ private static final String bufferClass = "java/lang/StringBuffer";
+ private static final String stringClass = "java/lang/String";
+
+ private static final VarType builderType = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/StringBuilder");
+ private static final VarType bufferType = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/StringBuffer");
+
+
+ public static Exprent contractStringConcat(Exprent expr) {
+
+ Exprent exprTmp = null;
+ VarType cltype = null;
+
+ // first quick test
+ if (expr.type == Exprent.EXPRENT_INVOCATION) {
+ InvocationExprent iex = (InvocationExprent)expr;
+ if ("toString".equals(iex.getName())) {
+ if (builderClass.equals(iex.getClassname())) {
+ cltype = builderType;
+ }
+ else if (bufferClass.equals(iex.getClassname())) {
+ cltype = bufferType;
+ }
+ if (cltype != null) {
+ exprTmp = iex.getInstance();
+ }
+ }
+ }
+
+ if (exprTmp == null) {
+ return expr;
+ }
+
+
+ // iterate in depth, collecting possible operands
+ List<Exprent> lstOperands = new ArrayList<Exprent>();
+
+ while (true) {
+
+ int found = 0;
+
+ switch (exprTmp.type) {
+ case Exprent.EXPRENT_INVOCATION:
+ InvocationExprent iex = (InvocationExprent)exprTmp;
+ if (isAppendConcat(iex, cltype)) {
+ lstOperands.add(0, iex.getLstParameters().get(0));
+ exprTmp = iex.getInstance();
+ found = 1;
+ }
+ break;
+ case Exprent.EXPRENT_NEW:
+ NewExprent nex = (NewExprent)exprTmp;
+ if (isNewConcat(nex, cltype)) {
+ VarType[] params = nex.getConstructor().getDescriptor().params;
+ if (params.length == 1) {
+ lstOperands.add(0, nex.getConstructor().getLstParameters().get(0));
+ }
+ found = 2;
+ }
+ }
+
+ if (found == 0) {
+ return expr;
+ }
+ else if (found == 2) {
+ break;
+ }
+ }
+
+ int first2str = 0;
+ int index = 0;
+ while (index < lstOperands.size() && index < 2) {
+ if (lstOperands.get(index).getExprType().equals(VarType.VARTYPE_STRING)) {
+ first2str |= (index + 1);
+ }
+ index++;
+ }
+
+ if (first2str == 0) {
+ lstOperands.add(0, new ConstExprent(VarType.VARTYPE_STRING, ""));
+ }
+
+ // remove redundant String.valueOf
+ for (int i = 0; i < lstOperands.size(); i++) {
+ Exprent rep = removeStringValueOf(lstOperands.get(i));
+
+ boolean ok = (i > 1);
+ if (!ok) {
+ boolean isstr = rep.getExprType().equals(VarType.VARTYPE_STRING);
+ ok = isstr || first2str != i + 1;
+
+ if (i == 0) {
+ first2str &= 2;
+ }
+ }
+
+ if (ok) {
+ lstOperands.set(i, rep);
+ }
+ }
+
+ // build exprent to return
+ Exprent func = lstOperands.get(0);
+
+ for (int i = 1; i < lstOperands.size(); i++) {
+ List<Exprent> lstTmp = new ArrayList<Exprent>();
+ lstTmp.add(func);
+ lstTmp.add(lstOperands.get(i));
+ func = new FunctionExprent(FunctionExprent.FUNCTION_STRCONCAT, lstTmp);
+ }
+
+ return func;
+ }
+
+ private static boolean isAppendConcat(InvocationExprent expr, VarType cltype) {
+
+ if ("append".equals(expr.getName())) {
+ MethodDescriptor md = expr.getDescriptor();
+ if (md.ret.equals(cltype) && md.params.length == 1) {
+ VarType param = md.params[0];
+ switch (param.type) {
+ case CodeConstants.TYPE_OBJECT:
+ if (!param.equals(VarType.VARTYPE_STRING) &&
+ !param.equals(VarType.VARTYPE_OBJECT)) {
+ break;
+ }
+ case CodeConstants.TYPE_BOOLEAN:
+ case CodeConstants.TYPE_CHAR:
+ case CodeConstants.TYPE_DOUBLE:
+ case CodeConstants.TYPE_FLOAT:
+ case CodeConstants.TYPE_INT:
+ case CodeConstants.TYPE_LONG:
+ return true;
+ default:
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private static boolean isNewConcat(NewExprent expr, VarType cltype) {
+
+ if (expr.getNewtype().equals(cltype)) {
+ VarType[] params = expr.getConstructor().getDescriptor().params;
+ if (params.length == 0 || (params.length == 1 &&
+ params[0].equals(VarType.VARTYPE_STRING))) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static Exprent removeStringValueOf(Exprent exprent) {
+
+ if (exprent.type == Exprent.EXPRENT_INVOCATION) {
+ InvocationExprent iex = (InvocationExprent)exprent;
+ if ("valueOf".equals(iex.getName()) && stringClass.equals(iex.getClassname())) {
+ MethodDescriptor md = iex.getDescriptor();
+ if (md.params.length == 1) {
+ VarType param = md.params[0];
+ switch (param.type) {
+ case CodeConstants.TYPE_OBJECT:
+ if (!param.equals(VarType.VARTYPE_OBJECT)) {
+ break;
+ }
+ case CodeConstants.TYPE_BOOLEAN:
+ case CodeConstants.TYPE_CHAR:
+ case CodeConstants.TYPE_DOUBLE:
+ case CodeConstants.TYPE_FLOAT:
+ case CodeConstants.TYPE_INT:
+ case CodeConstants.TYPE_LONG:
+ return iex.getLstParameters().get(0);
+ }
+ }
+ }
+ }
+
+ return exprent;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/DecHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/DecHelper.java
new file mode 100644
index 000000000000..9c719dbc9141
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/DecHelper.java
@@ -0,0 +1,218 @@
+/*
+ * 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.modules.decompiler;
+
+import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
+
+import java.util.*;
+
+
+public class DecHelper {
+
+ public static boolean checkStatementExceptions(List<Statement> lst) {
+
+ Set<Statement> all = new HashSet<Statement>(lst);
+
+ Set<Statement> handlers = new HashSet<Statement>();
+ Set<Statement> intersection = null;
+
+ for (Statement stat : lst) {
+ Set<Statement> setNew = stat.getNeighboursSet(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_FORWARD);
+
+ if (intersection == null) {
+ intersection = setNew;
+ }
+ else {
+ HashSet<Statement> interclone = new HashSet<Statement>(intersection);
+ interclone.removeAll(setNew);
+
+ intersection.retainAll(setNew);
+
+ setNew.removeAll(intersection);
+
+ handlers.addAll(interclone);
+ handlers.addAll(setNew);
+ }
+ }
+
+ for (Statement stat : handlers) {
+ if (!all.contains(stat) || !all.containsAll(stat.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_BACKWARD))) {
+ return false;
+ }
+ }
+
+ // check for other handlers (excluding head)
+ for (int i = 1; i < lst.size(); i++) {
+ Statement stat = lst.get(i);
+ if (!stat.getPredecessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty() && !handlers.contains(stat)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static boolean isChoiceStatement(Statement head, List<Statement> lst) {
+
+ Statement post = null;
+
+ Set<Statement> setDest = head.getNeighboursSet(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD);
+
+ if (setDest.contains(head)) {
+ return false;
+ }
+
+ while (true) {
+
+ lst.clear();
+
+ boolean repeat = false;
+
+ setDest.remove(post);
+
+ for (Statement stat : setDest) {
+ if (stat.getLastBasicType() != Statement.LASTBASICTYPE_GENERAL) {
+ if (post == null) {
+ post = stat;
+ repeat = true;
+ break;
+ }
+ else {
+ return false;
+ }
+ }
+
+ // preds
+ Set<Statement> setPred = stat.getNeighboursSet(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD);
+ setPred.remove(head);
+ if (setPred.contains(stat)) {
+ return false;
+ }
+
+ if (!setDest.containsAll(setPred) || setPred.size() > 1) {
+ if (post == null) {
+ post = stat;
+ repeat = true;
+ break;
+ }
+ else {
+ return false;
+ }
+ }
+ else if (setPred.size() == 1) {
+ Statement pred = setPred.iterator().next();
+ while (lst.contains(pred)) {
+ Set<Statement> setPredTemp = pred.getNeighboursSet(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD);
+ setPredTemp.remove(head);
+
+ if (!setPredTemp.isEmpty()) { // at most 1 predecessor
+ pred = setPredTemp.iterator().next();
+ if (pred == stat) {
+ return false; // loop found
+ }
+ }
+ else {
+ break;
+ }
+ }
+ }
+
+ // succs
+ List<StatEdge> lstEdges = stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL);
+ if (lstEdges.size() > 1) {
+ Set<Statement> setSucc = stat.getNeighboursSet(Statement.STATEDGE_DIRECT_ALL, Statement.DIRECTION_FORWARD);
+ setSucc.retainAll(setDest);
+
+ if (setSucc.size() > 0) {
+ return false;
+ }
+ else {
+ if (post == null) {
+ post = stat;
+ repeat = true;
+ break;
+ }
+ else {
+ return false;
+ }
+ }
+ }
+ else if (lstEdges.size() == 1) {
+
+ StatEdge edge = lstEdges.get(0);
+ if (edge.getType() == StatEdge.TYPE_REGULAR) {
+ Statement statd = edge.getDestination();
+ if (head == statd) {
+ return false;
+ }
+ if (!setDest.contains(statd) && post != statd) {
+ if (post != null) {
+ return false;
+ }
+ else {
+ Set<Statement> set = statd.getNeighboursSet(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD);
+ if (set.size() > 1) {
+ post = statd;
+ repeat = true;
+ break;
+ }
+ else {
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ lst.add(stat);
+ }
+
+ if (!repeat) {
+ break;
+ }
+ }
+
+ lst.add(head);
+ lst.remove(post);
+
+ lst.add(0, post);
+
+ return true;
+ }
+
+
+ public static HashSet<Statement> getUniquePredExceptions(Statement head) {
+
+ HashSet<Statement> setHandlers = new HashSet<Statement>(head.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_FORWARD));
+
+ Iterator<Statement> it = setHandlers.iterator();
+ while (it.hasNext()) {
+ if (it.next().getPredecessorEdges(StatEdge.TYPE_EXCEPTION).size() > 1) {
+ it.remove();
+ }
+ }
+ return setHandlers;
+ }
+
+ public static List<Exprent> copyExprentList(List<Exprent> lst) {
+ List<Exprent> ret = new ArrayList<Exprent>();
+ for (Exprent expr : lst) {
+ ret.add(expr.copy());
+ }
+ return ret;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java
new file mode 100644
index 000000000000..3bb45840f91d
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java
@@ -0,0 +1,707 @@
+/*
+ * 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.modules.decompiler;
+
+import org.jetbrains.java.decompiler.code.cfg.BasicBlock;
+import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph;
+import org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG;
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
+import org.jetbrains.java.decompiler.modules.decompiler.decompose.FastExtendedPostdominanceHelper;
+import org.jetbrains.java.decompiler.modules.decompiler.deobfuscator.IrreducibleCFGDeobfuscator;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.*;
+import org.jetbrains.java.decompiler.util.FastFixedSetFactory;
+import org.jetbrains.java.decompiler.util.FastFixedSetFactory.FastFixedSet;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+import org.jetbrains.java.decompiler.util.VBStyleCollection;
+
+import java.util.*;
+
+public class DomHelper {
+
+
+ private static RootStatement graphToStatement(ControlFlowGraph graph) {
+
+ VBStyleCollection<Statement, Integer> stats = new VBStyleCollection<Statement, Integer>();
+ VBStyleCollection<BasicBlock, Integer> blocks = graph.getBlocks();
+
+ for (BasicBlock block : blocks) {
+ stats.addWithKey(new BasicBlockStatement(block), block.id);
+ }
+
+ BasicBlock firstblock = graph.getFirst();
+ // head statement
+ Statement firstst = stats.getWithKey(firstblock.id);
+ // dummy exit statement
+ Statement dummyexit = new Statement();
+ dummyexit.type = Statement.TYPE_DUMMYEXIT;
+
+ Statement general;
+ if (stats.size() > 1 || firstblock.isSuccessor(firstblock)) { // multiple basic blocks or an infinite loop of one block
+ general = new GeneralStatement(firstst, stats, null);
+ }
+ else { // one straightforward basic block
+ RootStatement root = new RootStatement(firstst, dummyexit);
+ firstst.addSuccessor(new StatEdge(StatEdge.TYPE_BREAK, firstst, dummyexit, root));
+
+ return root;
+ }
+
+ for (BasicBlock block : blocks) {
+ Statement stat = stats.getWithKey(block.id);
+
+ for (BasicBlock succ : block.getSuccs()) {
+ Statement stsucc = stats.getWithKey(succ.id);
+
+ int type;
+ if (stsucc == firstst) {
+ type = StatEdge.TYPE_CONTINUE;
+ }
+ else if (graph.getFinallyExits().contains(block)) {
+ type = StatEdge.TYPE_FINALLYEXIT;
+ stsucc = dummyexit;
+ }
+ else if (succ.id == graph.getLast().id) {
+ type = StatEdge.TYPE_BREAK;
+ stsucc = dummyexit;
+ }
+ else {
+ type = StatEdge.TYPE_REGULAR;
+ }
+
+ stat.addSuccessor(new StatEdge(type, stat, (type == StatEdge.TYPE_CONTINUE) ? general : stsucc,
+ (type == StatEdge.TYPE_REGULAR) ? null : general));
+ }
+
+ // exceptions edges
+ for (BasicBlock succex : block.getSuccExceptions()) {
+ Statement stsuccex = stats.getWithKey(succex.id);
+
+ ExceptionRangeCFG range = graph.getExceptionRange(succex, block);
+ if (!range.isCircular()) {
+ stat.addSuccessor(new StatEdge(stat, stsuccex, range.getExceptionTypes()));
+ }
+ }
+ }
+
+ general.buildContinueSet();
+ general.buildMonitorFlags();
+ return new RootStatement(general, dummyexit);
+ }
+
+ public static VBStyleCollection<List<Integer>, Integer> calcPostDominators(Statement container) {
+
+ HashMap<Statement, FastFixedSet<Statement>> lists = new HashMap<Statement, FastFixedSet<Statement>>();
+
+ StrongConnectivityHelper schelper = new StrongConnectivityHelper(container);
+ List<List<Statement>> components = schelper.getComponents();
+
+ List<Statement> lstStats = container.getPostReversePostOrderList(StrongConnectivityHelper.getExitReps(components));
+
+ FastFixedSetFactory<Statement> factory = new FastFixedSetFactory<Statement>(lstStats);
+
+ FastFixedSet<Statement> setFlagNodes = factory.spawnEmptySet();
+ setFlagNodes.setAllElements();
+
+ FastFixedSet<Statement> initSet = factory.spawnEmptySet();
+ initSet.setAllElements();
+
+ for (List<Statement> lst : components) {
+ FastFixedSet<Statement> tmpSet;
+
+ if (StrongConnectivityHelper.isExitComponent(lst)) {
+ tmpSet = factory.spawnEmptySet();
+ tmpSet.addAll(lst);
+ }
+ else {
+ tmpSet = initSet.getCopy();
+ }
+
+ for (Statement stat : lst) {
+ lists.put(stat, tmpSet);
+ }
+ }
+
+ do {
+
+ for (Statement stat : lstStats) {
+
+ if (!setFlagNodes.contains(stat)) {
+ continue;
+ }
+ setFlagNodes.remove(stat);
+
+ FastFixedSet<Statement> doms = lists.get(stat);
+ FastFixedSet<Statement> domsSuccs = factory.spawnEmptySet();
+
+ List<Statement> lstSuccs = stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD);
+ for (int j = 0; j < lstSuccs.size(); j++) {
+ Statement succ = lstSuccs.get(j);
+ FastFixedSet<Statement> succlst = lists.get(succ);
+
+ if (j == 0) {
+ domsSuccs.union(succlst);
+ }
+ else {
+ domsSuccs.intersection(succlst);
+ }
+ }
+
+ if (!domsSuccs.contains(stat)) {
+ domsSuccs.add(stat);
+ }
+
+ if (!InterpreterUtil.equalObjects(domsSuccs, doms)) {
+
+ lists.put(stat, domsSuccs);
+
+ List<Statement> lstPreds = stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD);
+ for (Statement pred : lstPreds) {
+ setFlagNodes.add(pred);
+ }
+ }
+ }
+ }
+ while (!setFlagNodes.isEmpty());
+
+ VBStyleCollection<List<Integer>, Integer> ret = new VBStyleCollection<List<Integer>, Integer>();
+ List<Statement> lstRevPost = container.getReversePostOrderList(); // sort order crucial!
+
+ final HashMap<Integer, Integer> mapSortOrder = new HashMap<Integer, Integer>();
+ for (int i = 0; i < lstRevPost.size(); i++) {
+ mapSortOrder.put(lstRevPost.get(i).id, i);
+ }
+
+ for (Statement st : lstStats) {
+
+ List<Integer> lstPosts = new ArrayList<Integer>();
+ for (Statement stt : lists.get(st)) {
+ lstPosts.add(stt.id);
+ }
+
+ Collections.sort(lstPosts, new Comparator<Integer>() {
+ public int compare(Integer o1, Integer o2) {
+ return mapSortOrder.get(o1).compareTo(mapSortOrder.get(o2));
+ }
+ });
+
+ if (lstPosts.size() > 1 && lstPosts.get(0).intValue() == st.id) {
+ lstPosts.add(lstPosts.remove(0));
+ }
+
+ ret.addWithKey(lstPosts, st.id);
+ }
+
+ return ret;
+ }
+
+ public static RootStatement parseGraph(ControlFlowGraph graph) {
+
+ RootStatement root = graphToStatement(graph);
+
+ if (!processStatement(root, new HashMap<Integer, Set<Integer>>())) {
+ DecompilerContext.getLogger().writeMessage("parsing failure!", IFernflowerLogger.Severity.ERROR);
+
+ // try {
+ // DotExporter.toDotFile(root.getFirst().getStats().get(13), new File("c:\\Temp\\stat1.dot"));
+ // } catch (Exception ex) {
+ // ex.printStackTrace();
+ // }
+ throw new RuntimeException("parsing failure!");
+ }
+
+ LabelHelper.lowContinueLabels(root, new HashSet<StatEdge>());
+
+ SequenceHelper.condenseSequences(root);
+ root.buildMonitorFlags();
+
+ // build synchronized statements
+ buildSynchronized(root);
+
+ return root;
+ }
+
+ public static void removeSynchronizedHandler(Statement stat) {
+
+ for (Statement st : stat.getStats()) {
+ removeSynchronizedHandler(st);
+ }
+
+ if (stat.type == Statement.TYPE_SYNCRONIZED) {
+ ((SynchronizedStatement)stat).removeExc();
+ }
+ }
+
+
+ private static void buildSynchronized(Statement stat) {
+
+ for (Statement st : stat.getStats()) {
+ buildSynchronized(st);
+ }
+
+ if (stat.type == Statement.TYPE_SEQUENCE) {
+
+ while (true) {
+
+ boolean found = false;
+
+ List<Statement> lst = stat.getStats();
+ for (int i = 0; i < lst.size() - 1; i++) {
+ Statement current = lst.get(i); // basic block
+
+ if (current.isMonitorEnter()) {
+
+ Statement next = lst.get(i + 1);
+ Statement nextDirect = next;
+
+ while (next.type == Statement.TYPE_SEQUENCE) {
+ next = next.getFirst();
+ }
+
+ if (next.type == Statement.TYPE_CATCHALL) {
+
+ CatchAllStatement ca = (CatchAllStatement)next;
+
+ if (ca.getFirst().isContainsMonitorExit() && ca.getHandler().isContainsMonitorExit()) {
+
+ // remove the head block from sequence
+ current.removeSuccessor(current.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL).get(0));
+
+ for (StatEdge edge : current.getPredecessorEdges(Statement.STATEDGE_DIRECT_ALL)) {
+ current.removePredecessor(edge);
+ edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, nextDirect);
+ nextDirect.addPredecessor(edge);
+ }
+
+ stat.getStats().removeWithKey(current.id);
+ stat.setFirst(stat.getStats().get(0));
+
+ // new statement
+ SynchronizedStatement sync = new SynchronizedStatement(current, ca.getFirst(), ca.getHandler());
+ sync.setAllParent();
+
+ for (StatEdge edge : new HashSet<StatEdge>(ca.getLabelEdges())) {
+ sync.addLabeledEdge(edge);
+ }
+
+ current.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, current, ca.getFirst()));
+
+ ca.getParent().replaceStatement(ca, sync);
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!found) {
+ break;
+ }
+ }
+ }
+ }
+
+ private static boolean processStatement(Statement general, HashMap<Integer, Set<Integer>> mapExtPost) {
+
+ if (general.type == Statement.TYPE_ROOT) {
+ Statement stat = general.getFirst();
+ if (stat.type != Statement.TYPE_GENERAL) {
+ return true;
+ }
+ else {
+ boolean complete = processStatement(stat, mapExtPost);
+ if (complete) {
+ // replace general purpose statement with simple one
+ general.replaceStatement(stat, stat.getFirst());
+ }
+ return complete;
+ }
+ }
+
+ boolean mapRefreshed = mapExtPost.isEmpty();
+
+ for (int mapstage = 0; mapstage < 2; mapstage++) {
+
+ for (int reducibility = 0;
+ reducibility < 5;
+ reducibility++) { // FIXME: implement proper node splitting. For now up to 5 nodes in sequence are splitted.
+
+ if (reducibility > 0) {
+
+ // try {
+ // DotExporter.toDotFile(general, new File("c:\\Temp\\stat1.dot"));
+ // } catch(Exception ex) {ex.printStackTrace();}
+
+ // take care of irreducible control flow graphs
+ if (IrreducibleCFGDeobfuscator.isStatementIrreducible(general)) {
+ if (!IrreducibleCFGDeobfuscator.splitIrreducibleNode(general)) {
+ DecompilerContext.getLogger().writeMessage("Irreducible statement cannot be decomposed!", IFernflowerLogger.Severity.ERROR);
+ break;
+ }
+ }
+ else {
+ if (mapstage == 2 || mapRefreshed) { // last chance lost
+ DecompilerContext.getLogger().writeMessage("Statement cannot be decomposed although reducible!",
+ IFernflowerLogger.Severity.ERROR);
+ }
+ break;
+ }
+
+ // try {
+ // DotExporter.toDotFile(general, new File("c:\\Temp\\stat1.dot"));
+ // } catch(Exception ex) {ex.printStackTrace();}
+
+ mapExtPost = new HashMap<Integer, Set<Integer>>();
+ mapRefreshed = true;
+ }
+
+ for (int i = 0; i < 2; i++) {
+
+ boolean forceall = i != 0;
+
+ while (true) {
+
+ if (findSimpleStatements(general, mapExtPost)) {
+ reducibility = 0;
+ }
+
+ if (general.type == Statement.TYPE_PLACEHOLDER) {
+ return true;
+ }
+
+ Statement stat = findGeneralStatement(general, forceall, mapExtPost);
+
+ if (stat != null) {
+ boolean complete = processStatement(stat, general.getFirst() == stat ? mapExtPost : new HashMap<Integer, Set<Integer>>());
+
+ if (complete) {
+ // replace general purpose statement with simple one
+ general.replaceStatement(stat, stat.getFirst());
+ }
+ else {
+ return false;
+ }
+
+ mapExtPost = new HashMap<Integer, Set<Integer>>();
+ mapRefreshed = true;
+ reducibility = 0;
+ }
+ else {
+ break;
+ }
+ }
+ }
+
+ // try {
+ // DotExporter.toDotFile(general, new File("c:\\Temp\\stat1.dot"));
+ // } catch (Exception ex) {
+ // ex.printStackTrace();
+ // }
+ }
+
+ if (mapRefreshed) {
+ break;
+ }
+ else {
+ mapExtPost = new HashMap<Integer, Set<Integer>>();
+ }
+ }
+
+ return false;
+ }
+
+ private static Statement findGeneralStatement(Statement stat, boolean forceall, HashMap<Integer, Set<Integer>> mapExtPost) {
+
+ VBStyleCollection<Statement, Integer> stats = stat.getStats();
+ VBStyleCollection<List<Integer>, Integer> vbPost;
+
+ if (mapExtPost.isEmpty()) {
+ FastExtendedPostdominanceHelper extpost = new FastExtendedPostdominanceHelper();
+ mapExtPost.putAll(extpost.getExtendedPostdominators(stat));
+ }
+
+ if (forceall) {
+ vbPost = new VBStyleCollection<List<Integer>, Integer>();
+ List<Statement> lstAll = stat.getPostReversePostOrderList();
+
+ for (Statement st : lstAll) {
+ Set<Integer> set = mapExtPost.get(st.id);
+ if (set != null) {
+ vbPost.addWithKey(new ArrayList<Integer>(set), st.id); // FIXME: sort order!!
+ }
+ }
+
+ // tail statements
+ Set<Integer> setFirst = mapExtPost.get(stat.getFirst().id);
+ if (setFirst != null) {
+ for (Integer id : setFirst) {
+ List<Integer> lst = vbPost.getWithKey(id);
+ if (lst == null) {
+ vbPost.addWithKey(lst = new ArrayList<Integer>(), id);
+ }
+ lst.add(id);
+ }
+ }
+ }
+ else {
+ vbPost = calcPostDominators(stat);
+ }
+
+ for (int k = 0; k < vbPost.size(); k++) {
+
+ Integer headid = vbPost.getKey(k);
+ List<Integer> posts = vbPost.get(k);
+
+ if (!mapExtPost.containsKey(headid) &&
+ !(posts.size() == 1 && posts.get(0).equals(headid))) {
+ continue;
+ }
+
+ Statement head = stats.getWithKey(headid);
+
+ Set<Integer> setExtPosts = mapExtPost.get(headid);
+
+ for (int i = 0; i < posts.size(); i++) {
+
+ Integer postid = posts.get(i);
+ if (!postid.equals(headid) && !setExtPosts.contains(postid)) {
+ continue;
+ }
+
+ Statement post = stats.getWithKey(postid);
+
+ if (post == null) { // possible in case of an inherited postdominance set
+ continue;
+ }
+
+ boolean same = (post == head);
+
+ HashSet<Statement> setNodes = new HashSet<Statement>();
+ HashSet<Statement> setPreds = new HashSet<Statement>();
+
+ // collect statement nodes
+ HashSet<Statement> setHandlers = new HashSet<Statement>();
+ setHandlers.add(head);
+ while (true) {
+
+ boolean hdfound = false;
+ for (Statement handler : setHandlers) {
+ if (setNodes.contains(handler)) {
+ continue;
+ }
+
+ boolean addhd = (setNodes.size() == 0); // first handler == head
+ if (!addhd) {
+ List<Statement> hdsupp = handler.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_BACKWARD);
+ addhd = (setNodes.containsAll(hdsupp) && (setNodes.size() > hdsupp.size()
+ || setNodes.size() == 1)); // strict subset
+ }
+
+ if (addhd) {
+ LinkedList<Statement> lstStack = new LinkedList<Statement>();
+ lstStack.add(handler);
+
+ while (!lstStack.isEmpty()) {
+ Statement st = lstStack.remove(0);
+
+ if (!(setNodes.contains(st) || (!same && st == post))) {
+ setNodes.add(st);
+ if (st != head) {
+ // record predeccessors except for the head
+ setPreds.addAll(st.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD));
+ }
+
+ // put successors on the stack
+ lstStack.addAll(st.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD));
+
+ // exception edges
+ setHandlers.addAll(st.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_FORWARD));
+ }
+ }
+
+ hdfound = true;
+ setHandlers.remove(handler);
+ break;
+ }
+ }
+
+ if (!hdfound) {
+ break;
+ }
+ }
+
+ // check exception handlers
+ setHandlers.clear();
+ for (Statement st : setNodes) {
+ setHandlers.addAll(st.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_FORWARD));
+ }
+ setHandlers.removeAll(setNodes);
+
+ boolean excok = true;
+ for (Statement handler : setHandlers) {
+ if (!handler.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_BACKWARD).containsAll(setNodes)) {
+ excok = false;
+ break;
+ }
+ }
+
+ // build statement and return
+ if (excok) {
+ Statement res = null;
+
+ setPreds.removeAll(setNodes);
+ if (setPreds.size() == 0) {
+ if ((setNodes.size() > 1 ||
+ head.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD).contains(head))
+ && setNodes.size() < stats.size()) {
+ if (checkSynchronizedCompleteness(head, setNodes)) {
+ res = new GeneralStatement(head, setNodes, same ? null : post);
+ stat.collapseNodesToStatement(res);
+
+ return res;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private static boolean checkSynchronizedCompleteness(Statement head, HashSet<Statement> setNodes) {
+
+ // check exit nodes
+ for (Statement stat : setNodes) {
+ if (stat.isMonitorEnter()) {
+ List<StatEdge> lstSuccs = stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL);
+ if (lstSuccs.size() != 1 || lstSuccs.get(0).getType() != StatEdge.TYPE_REGULAR) {
+ return false;
+ }
+
+ if (!setNodes.contains(lstSuccs.get(0).getDestination())) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private static boolean findSimpleStatements(Statement stat, HashMap<Integer, Set<Integer>> mapExtPost) {
+
+ boolean found, success = false;
+
+ do {
+ found = false;
+
+ List<Statement> lstStats = stat.getPostReversePostOrderList();
+ for (Statement st : lstStats) {
+
+ Statement result = detectStatement(st);
+
+ if (result != null) {
+
+ if (stat.type == Statement.TYPE_GENERAL && result.getFirst() == stat.getFirst() &&
+ stat.getStats().size() == result.getStats().size()) {
+ // mark general statement
+ stat.type = Statement.TYPE_PLACEHOLDER;
+ }
+
+ stat.collapseNodesToStatement(result);
+
+ // update the postdominator map
+ if (!mapExtPost.isEmpty()) {
+ HashSet<Integer> setOldNodes = new HashSet<Integer>();
+ for (Statement old : result.getStats()) {
+ setOldNodes.add(old.id);
+ }
+
+ Integer newid = result.id;
+
+ for (Integer key : new ArrayList<Integer>(mapExtPost.keySet())) {
+ Set<Integer> set = mapExtPost.get(key);
+
+ int oldsize = set.size();
+ set.removeAll(setOldNodes);
+
+ if (setOldNodes.contains(key)) {
+ Set<Integer> setNew = mapExtPost.get(newid);
+ if (setNew == null) {
+ mapExtPost.put(newid, setNew = new HashSet<Integer>());
+ }
+ setNew.addAll(set);
+
+ mapExtPost.remove(key);
+ }
+ else {
+ if (set.size() < oldsize) {
+ set.add(newid);
+ }
+ }
+ }
+ }
+
+
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ success = true;
+ }
+ }
+ while (found);
+
+ return success;
+ }
+
+
+ private static Statement detectStatement(Statement head) {
+
+ Statement res;
+
+ if ((res = DoStatement.isHead(head)) != null) {
+ return res;
+ }
+
+ if ((res = SwitchStatement.isHead(head)) != null) {
+ return res;
+ }
+
+ if ((res = IfStatement.isHead(head)) != null) {
+ return res;
+ }
+
+ // synchronized statements will be identified later
+ // right now they are recognized as catchall
+
+ if ((res = SequenceStatement.isHead2Block(head)) != null) {
+ return res;
+ }
+
+ if ((res = CatchStatement.isHead(head)) != null) {
+ return res;
+ }
+
+ if ((res = CatchAllStatement.isHead(head)) != null) {
+ return res;
+ }
+
+ return null;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/EliminateLoopsHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/EliminateLoopsHelper.java
new file mode 100644
index 000000000000..16f7da084534
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/EliminateLoopsHelper.java
@@ -0,0 +1,214 @@
+/*
+ * 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.modules.decompiler;
+
+import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+
+public class EliminateLoopsHelper {
+
+
+ // public static boolean eliminateLoops(Statement root) {
+ //
+ // boolean ret = eliminateLoopsRec(root);
+ //
+ // if(ret) {
+ // SequenceHelper.condenseSequences(root);
+ //
+ // HashSet<Integer> setReorderedIfs = new HashSet<Integer>();
+ //
+ // SimplifyExprentsHelper sehelper = new SimplifyExprentsHelper(false);
+ // while(sehelper.simplifyStackVarsStatement(root, setReorderedIfs, null)) {
+ // SequenceHelper.condenseSequences(root);
+ // }
+ // }
+ //
+ // return ret;
+ // }
+
+ private static boolean eliminateLoopsRec(Statement stat) {
+
+ for (Statement st : stat.getStats()) {
+ if (eliminateLoopsRec(st)) {
+ return true;
+ }
+ }
+
+ if (stat.type == Statement.TYPE_DO && isLoopRedundant((DoStatement)stat)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private static boolean isLoopRedundant(DoStatement loop) {
+
+ if (loop.getLooptype() != DoStatement.LOOP_DO) {
+ return false;
+ }
+
+ // get parent loop if exists
+ Statement parentloop = loop.getParent();
+ while (parentloop != null && parentloop.type != Statement.TYPE_DO) {
+ parentloop = parentloop.getParent();
+ }
+
+ if (parentloop == null || parentloop.getBasichead() != loop.getBasichead()) {
+ return false;
+ }
+
+ // collect relevant break edges
+ List<StatEdge> lstBreakEdges = new ArrayList<StatEdge>();
+ for (StatEdge edge : loop.getLabelEdges()) {
+ if (edge.getType() == StatEdge.TYPE_BREAK) { // all break edges are explicit because of LOOP_DO type
+ lstBreakEdges.add(edge);
+ }
+ }
+
+
+ Statement loopcontent = loop.getFirst();
+
+ boolean firstok = loopcontent.getAllSuccessorEdges().isEmpty();
+ if (!firstok) {
+ StatEdge edge = loopcontent.getAllSuccessorEdges().get(0);
+ firstok = (edge.closure == loop && edge.getType() == StatEdge.TYPE_BREAK);
+ if (firstok) {
+ lstBreakEdges.remove(edge);
+ }
+ }
+
+
+ if (!lstBreakEdges.isEmpty()) {
+ if (firstok) {
+
+ HashMap<Integer, Boolean> statLabeled = new HashMap<Integer, Boolean>();
+ List<Statement> lstEdgeClosures = new ArrayList<Statement>();
+
+ for (StatEdge edge : lstBreakEdges) {
+ Statement minclosure = LowBreakHelper.getMinClosure(loopcontent, edge.getSource());
+ lstEdgeClosures.add(minclosure);
+ }
+
+ int precount = loop.isLabeled() ? 1 : 0;
+ for (Statement st : lstEdgeClosures) {
+ if (!statLabeled.containsKey(st.id)) {
+ boolean btemp = st.isLabeled();
+ precount += btemp ? 1 : 0;
+ statLabeled.put(st.id, btemp);
+ }
+ }
+
+ for (int i = 0; i < lstBreakEdges.size(); i++) {
+ Statement st = lstEdgeClosures.get(i);
+ statLabeled.put(st.id, LowBreakHelper.isBreakEdgeLabeled(lstBreakEdges.get(i).getSource(), st) | statLabeled.get(st.id));
+ }
+
+ for (int i = 0; i < lstBreakEdges.size(); i++) {
+ lstEdgeClosures.set(i, getMaxBreakLift(lstEdgeClosures.get(i), lstBreakEdges.get(i), statLabeled, loop));
+ }
+
+ statLabeled.clear();
+ for (Statement st : lstEdgeClosures) {
+ statLabeled.put(st.id, st.isLabeled());
+ }
+
+ for (int i = 0; i < lstBreakEdges.size(); i++) {
+ Statement st = lstEdgeClosures.get(i);
+ statLabeled.put(st.id, LowBreakHelper.isBreakEdgeLabeled(lstBreakEdges.get(i).getSource(), st) | statLabeled.get(st.id));
+ }
+
+ int postcount = 0;
+ for (Boolean val : statLabeled.values()) {
+ postcount += val ? 1 : 0;
+ }
+
+ if (precount <= postcount) {
+ return false;
+ }
+ else {
+ for (int i = 0; i < lstBreakEdges.size(); i++) {
+ lstEdgeClosures.get(i).addLabeledEdge(lstBreakEdges.get(i));
+ }
+ }
+ }
+ else {
+ return false;
+ }
+ }
+
+ eliminateLoop(loop, parentloop);
+
+ return true;
+ }
+
+ private static Statement getMaxBreakLift(Statement stat, StatEdge edge, HashMap<Integer, Boolean> statLabeled, Statement max) {
+
+ Statement closure = stat;
+ Statement newclosure = stat;
+
+ while ((newclosure = getNextBreakLift(newclosure, edge, statLabeled, max)) != null) {
+ closure = newclosure;
+ }
+
+ return closure;
+ }
+
+ private static Statement getNextBreakLift(Statement stat, StatEdge edge, HashMap<Integer, Boolean> statLabeled, Statement max) {
+
+ Statement closure = stat.getParent();
+
+ while (closure != null && closure != max && !closure.containsStatementStrict(edge.getDestination())) {
+
+ boolean edge_labeled = LowBreakHelper.isBreakEdgeLabeled(edge.getSource(), closure);
+ boolean stat_labeled = statLabeled.containsKey(closure.id) ? statLabeled.get(closure.id) : closure.isLabeled();
+
+ if (stat_labeled || !edge_labeled) {
+ return closure;
+ }
+
+ closure = closure.getParent();
+ }
+
+ return null;
+ }
+
+ private static void eliminateLoop(Statement loop, Statement parentloop) {
+
+ // move continue edges to the parent loop
+ List<StatEdge> lst = new ArrayList<StatEdge>(loop.getLabelEdges());
+ for (StatEdge edge : lst) {
+ loop.removePredecessor(edge);
+ edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, parentloop);
+ parentloop.addPredecessor(edge);
+
+ parentloop.addLabeledEdge(edge);
+ }
+
+ // remove the last break edge, if exists
+ Statement loopcontent = loop.getFirst();
+ if (!loopcontent.getAllSuccessorEdges().isEmpty()) {
+ loopcontent.removeSuccessor(loopcontent.getAllSuccessorEdges().get(0));
+ }
+
+ // replace loop with its content
+ loop.getParent().replaceStatement(loop, loopcontent);
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java
new file mode 100644
index 000000000000..af7358456ce5
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java
@@ -0,0 +1,345 @@
+/*
+ * 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.modules.decompiler;
+
+import org.jetbrains.java.decompiler.code.cfg.BasicBlock;
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.*;
+import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+public class ExitHelper {
+
+
+ public static boolean condenseExits(RootStatement root) {
+
+ int changed = integrateExits(root);
+
+ if (changed > 0) {
+
+ cleanUpUnreachableBlocks(root);
+
+ SequenceHelper.condenseSequences(root);
+ }
+
+ return (changed > 0);
+ }
+
+
+ private static void cleanUpUnreachableBlocks(Statement stat) {
+
+ boolean found;
+ do {
+
+ found = false;
+
+ for (int i = 0; i < stat.getStats().size(); i++) {
+
+ Statement st = stat.getStats().get(i);
+
+ cleanUpUnreachableBlocks(st);
+
+ if (st.type == Statement.TYPE_SEQUENCE && st.getStats().size() > 1) {
+
+ Statement last = st.getStats().getLast();
+ Statement secondlast = st.getStats().get(st.getStats().size() - 2);
+
+ if (last.getExprents() == null || !last.getExprents().isEmpty()) {
+ if (!secondlast.hasBasicSuccEdge()) {
+
+ Set<Statement> set = last.getNeighboursSet(Statement.STATEDGE_DIRECT_ALL, Statement.DIRECTION_BACKWARD);
+ set.remove(secondlast);
+
+ if (set.isEmpty()) {
+ last.setExprents(new ArrayList<Exprent>());
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ while (found);
+ }
+
+
+ private static int integrateExits(Statement stat) {
+
+ int ret = 0;
+ Statement dest = null;
+
+ if (stat.getExprents() == null) {
+
+ while (true) {
+
+ int changed = 0;
+
+ for (Statement st : stat.getStats()) {
+ changed = integrateExits(st);
+ if (changed > 0) {
+ ret = 1;
+ break;
+ }
+ }
+
+ if (changed == 0) {
+ break;
+ }
+ }
+
+
+ switch (stat.type) {
+ case Statement.TYPE_IF:
+ IfStatement ifst = (IfStatement)stat;
+ if (ifst.getIfstat() == null) {
+ StatEdge ifedge = ifst.getIfEdge();
+ dest = isExitEdge(ifedge);
+ if (dest != null) {
+ BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock(
+ DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)));
+ bstat.setExprents(DecHelper.copyExprentList(dest.getExprents()));
+
+ ifst.getFirst().removeSuccessor(ifedge);
+ StatEdge newedge = new StatEdge(StatEdge.TYPE_REGULAR, ifst.getFirst(), bstat);
+ ifst.getFirst().addSuccessor(newedge);
+ ifst.setIfEdge(newedge);
+ ifst.setIfstat(bstat);
+ ifst.getStats().addWithKey(bstat, bstat.id);
+ bstat.setParent(ifst);
+
+ StatEdge oldexitedge = dest.getAllSuccessorEdges().get(0);
+ StatEdge newexitedge = new StatEdge(StatEdge.TYPE_BREAK, bstat, oldexitedge.getDestination());
+ bstat.addSuccessor(newexitedge);
+ oldexitedge.closure.addLabeledEdge(newexitedge);
+ ret = 1;
+ }
+ }
+ }
+ }
+
+
+ if (stat.getAllSuccessorEdges().size() == 1 &&
+ stat.getAllSuccessorEdges().get(0).getType() == StatEdge.TYPE_BREAK &&
+ stat.getLabelEdges().isEmpty()) {
+ Statement parent = stat.getParent();
+ if (stat != parent.getFirst() || (parent.type != Statement.TYPE_IF &&
+ parent.type != Statement.TYPE_SWITCH)) {
+
+ StatEdge destedge = stat.getAllSuccessorEdges().get(0);
+ dest = isExitEdge(destedge);
+ if (dest != null) {
+ stat.removeSuccessor(destedge);
+
+ BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock(
+ DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)));
+ bstat.setExprents(DecHelper.copyExprentList(dest.getExprents()));
+
+ StatEdge oldexitedge = dest.getAllSuccessorEdges().get(0);
+ StatEdge newexitedge = new StatEdge(StatEdge.TYPE_BREAK, bstat, oldexitedge.getDestination());
+ bstat.addSuccessor(newexitedge);
+ oldexitedge.closure.addLabeledEdge(newexitedge);
+
+ SequenceStatement block = new SequenceStatement(Arrays.asList(stat, bstat));
+ block.setAllParent();
+
+ parent.replaceStatement(stat, block);
+ // LabelHelper.lowContinueLabels not applicable because of forward continue edges
+ // LabelHelper.lowContinueLabels(block, new HashSet<StatEdge>());
+ // do it by hand
+ for (StatEdge prededge : block.getPredecessorEdges(StatEdge.TYPE_CONTINUE)) {
+
+ block.removePredecessor(prededge);
+ prededge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, prededge, stat);
+ stat.addPredecessor(prededge);
+
+ stat.addLabeledEdge(prededge);
+ }
+
+
+ stat.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, stat, bstat));
+
+ for (StatEdge edge : dest.getAllPredecessorEdges()) {
+ if (!edge.explicit && stat.containsStatementStrict(edge.getSource()) &&
+ MergeHelper.isDirectPath(edge.getSource().getParent(), bstat)) {
+
+ dest.removePredecessor(edge);
+ edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, bstat);
+ bstat.addPredecessor(edge);
+
+ if (!stat.containsStatementStrict(edge.closure)) {
+ stat.addLabeledEdge(edge);
+ }
+ }
+ }
+
+ ret = 2;
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ private static Statement isExitEdge(StatEdge edge) {
+
+ Statement dest = edge.getDestination();
+
+ if (edge.getType() == StatEdge.TYPE_BREAK && dest.type == Statement.TYPE_BASICBLOCK
+ && edge.explicit && (edge.labeled || isOnlyEdge(edge))) {
+ List<Exprent> data = dest.getExprents();
+
+ if (data != null && data.size() == 1) {
+ if (data.get(0).type == Exprent.EXPRENT_EXIT) {
+ return dest;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private static boolean isOnlyEdge(StatEdge edge) {
+
+ Statement stat = edge.getDestination();
+
+ for (StatEdge ed : stat.getAllPredecessorEdges()) {
+ if (ed != edge) {
+ if (ed.getType() == StatEdge.TYPE_REGULAR) {
+ Statement source = ed.getSource();
+
+ if (source.type == Statement.TYPE_BASICBLOCK || (source.type == Statement.TYPE_IF &&
+ ((IfStatement)source).iftype == IfStatement.IFTYPE_IF) ||
+ (source.type == Statement.TYPE_DO && ((DoStatement)source).getLooptype() != DoStatement.LOOP_DO)) {
+ return false;
+ }
+ }
+ else {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public static boolean removeRedundantReturns(RootStatement root) {
+
+ boolean res = false;
+
+ for (StatEdge edge : root.getDummyExit().getAllPredecessorEdges()) {
+ if (!edge.explicit) {
+ Statement source = edge.getSource();
+ List<Exprent> lstExpr = source.getExprents();
+ if (lstExpr != null && !lstExpr.isEmpty()) {
+ Exprent expr = lstExpr.get(lstExpr.size() - 1);
+ if (expr.type == Exprent.EXPRENT_EXIT) {
+ ExitExprent ex = (ExitExprent)expr;
+ if (ex.getExittype() == ExitExprent.EXIT_RETURN && ex.getValue() == null) {
+ // remove redundant return
+ lstExpr.remove(lstExpr.size() - 1);
+ res = true;
+ }
+ }
+ }
+ }
+ }
+
+ return res;
+ }
+
+ public static boolean handleReturnFromInitializer(RootStatement root) {
+
+ boolean res = false;
+
+ Statement exit = root.getDummyExit();
+ Statement top = root.getFirst();
+ Statement newret = null;
+
+ boolean sharedcreated = false;
+
+ for (StatEdge edge : exit.getAllPredecessorEdges()) {
+ if (edge.explicit) {
+
+ if (!sharedcreated) {
+ newret = addSharedInitializerReturn(root);
+ sharedcreated = true;
+ }
+
+ Statement source = edge.getSource();
+ List<Exprent> lstExpr = source.getExprents();
+ if (lstExpr != null && !lstExpr.isEmpty()) {
+ Exprent expr = lstExpr.get(lstExpr.size() - 1);
+ if (expr.type == Exprent.EXPRENT_EXIT) {
+ ExitExprent ex = (ExitExprent)expr;
+ if (ex.getExittype() == ExitExprent.EXIT_RETURN && ex.getValue() == null) {
+ lstExpr.remove(lstExpr.size() - 1);
+
+ source.removeSuccessor(edge);
+ source.addSuccessor(new StatEdge(StatEdge.TYPE_BREAK, source, newret, top));
+
+ res = true;
+ }
+ }
+ }
+ }
+ }
+
+ return res;
+ }
+
+ private static Statement addSharedInitializerReturn(RootStatement root) {
+
+ Statement exit = root.getDummyExit();
+ Statement top = root.getFirst();
+
+ // build a new statement with the single instruction 'return'
+ BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock(
+ DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)));
+
+ ExitExprent retexpr = new ExitExprent(ExitExprent.EXIT_RETURN, null,
+ ((MethodDescriptor)DecompilerContext
+ .getProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR)).ret);
+ // a changeable list needed
+ bstat.setExprents(new ArrayList<Exprent>(Arrays.asList(new Exprent[]{retexpr})));
+
+ // build sequence to replace the former top statement
+ SequenceStatement seq = new SequenceStatement(Arrays.asList(top, bstat));
+ top.setParent(seq);
+ bstat.setParent(seq);
+ seq.setParent(root);
+
+ root.getStats().removeWithKey(top.id);
+ root.getStats().addWithKeyAndIndex(0, seq, seq.id);
+ root.setFirst(seq);
+
+ for (StatEdge succedge : top.getAllSuccessorEdges()) {
+ top.removeSuccessor(succedge);
+ }
+
+ top.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, top, bstat));
+ bstat.addSuccessor(new StatEdge(StatEdge.TYPE_BREAK, bstat, exit, seq));
+
+ return bstat;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java
new file mode 100644
index 000000000000..0dc660ddf722
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java
@@ -0,0 +1,913 @@
+/*
+ * 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.modules.decompiler;
+
+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.cfg.BasicBlock;
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
+import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph;
+import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode;
+import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper;
+import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper.FinallyPathWrapper;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.*;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
+import org.jetbrains.java.decompiler.struct.StructClass;
+import org.jetbrains.java.decompiler.struct.attr.StructBootstrapMethodsAttribute;
+import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;
+import org.jetbrains.java.decompiler.struct.consts.ConstantPool;
+import org.jetbrains.java.decompiler.struct.consts.LinkConstant;
+import org.jetbrains.java.decompiler.struct.consts.PooledConstant;
+import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;
+import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+
+import java.util.*;
+
+public class ExprProcessor implements CodeConstants {
+
+ public static final String UNDEFINED_TYPE_STRING = "<undefinedtype>";
+ public static final String UNKNOWN_TYPE_STRING = "<unknown>";
+ public static final String NULL_TYPE_STRING = "<null>";
+
+ private static final HashMap<Integer, Integer> mapConsts = new HashMap<Integer, Integer>();
+
+ static {
+
+ // mapConsts.put(new Integer(opc_i2l), new
+ // Integer(FunctionExprent.FUNCTION_I2L));
+ // mapConsts.put(new Integer(opc_i2f), new
+ // Integer(FunctionExprent.FUNCTION_I2F));
+ // mapConsts.put(new Integer(opc_i2d), new
+ // Integer(FunctionExprent.FUNCTION_I2D));
+ // mapConsts.put(new Integer(opc_l2i), new
+ // Integer(FunctionExprent.FUNCTION_L2I));
+ // mapConsts.put(new Integer(opc_l2f), new
+ // Integer(FunctionExprent.FUNCTION_L2F));
+ // mapConsts.put(new Integer(opc_l2d), new
+ // Integer(FunctionExprent.FUNCTION_L2D));
+ // mapConsts.put(new Integer(opc_f2i), new
+ // Integer(FunctionExprent.FUNCTION_F2I));
+ // mapConsts.put(new Integer(opc_f2l), new
+ // Integer(FunctionExprent.FUNCTION_F2L));
+ // mapConsts.put(new Integer(opc_f2d), new
+ // Integer(FunctionExprent.FUNCTION_F2D));
+ // mapConsts.put(new Integer(opc_d2i), new
+ // Integer(FunctionExprent.FUNCTION_D2I));
+ // mapConsts.put(new Integer(opc_d2l), new
+ // Integer(FunctionExprent.FUNCTION_D2L));
+ // mapConsts.put(new Integer(opc_d2f), new
+ // Integer(FunctionExprent.FUNCTION_D2F));
+ // mapConsts.put(new Integer(opc_i2b), new
+ // Integer(FunctionExprent.FUNCTION_I2B));
+ // mapConsts.put(new Integer(opc_i2c), new
+ // Integer(FunctionExprent.FUNCTION_I2C));
+ // mapConsts.put(new Integer(opc_i2s), new
+ // Integer(FunctionExprent.FUNCTION_I2S));
+
+ mapConsts.put(new Integer(opc_arraylength), new Integer(FunctionExprent.FUNCTION_ARRAYLENGTH));
+ mapConsts.put(new Integer(opc_checkcast), new Integer(FunctionExprent.FUNCTION_CAST));
+ mapConsts.put(new Integer(opc_instanceof), new Integer(FunctionExprent.FUNCTION_INSTANCEOF));
+ }
+
+ private static final VarType[] consts =
+ new VarType[]{VarType.VARTYPE_INT, VarType.VARTYPE_FLOAT, VarType.VARTYPE_LONG, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_CLASS,
+ VarType.VARTYPE_STRING};
+
+ private static final VarType[] vartypes =
+ new VarType[]{VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_OBJECT};
+
+ private static final VarType[] arrtypes =
+ new VarType[]{VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_OBJECT,
+ VarType.VARTYPE_BOOLEAN, VarType.VARTYPE_CHAR, VarType.VARTYPE_SHORT};
+
+ private static final int[] func1 =
+ new int[]{FunctionExprent.FUNCTION_ADD, FunctionExprent.FUNCTION_SUB, FunctionExprent.FUNCTION_MUL, FunctionExprent.FUNCTION_DIV,
+ FunctionExprent.FUNCTION_REM};
+
+ private static final int[] func2 =
+ new int[]{FunctionExprent.FUNCTION_SHL, FunctionExprent.FUNCTION_SHR, FunctionExprent.FUNCTION_USHR, FunctionExprent.FUNCTION_AND,
+ FunctionExprent.FUNCTION_OR, FunctionExprent.FUNCTION_XOR};
+
+ private static final int[] func3 =
+ new int[]{FunctionExprent.FUNCTION_I2L, FunctionExprent.FUNCTION_I2F, FunctionExprent.FUNCTION_I2D, FunctionExprent.FUNCTION_L2I,
+ FunctionExprent.FUNCTION_L2F, FunctionExprent.FUNCTION_L2D, FunctionExprent.FUNCTION_F2I, FunctionExprent.FUNCTION_F2L,
+ FunctionExprent.FUNCTION_F2D,
+ FunctionExprent.FUNCTION_D2I, FunctionExprent.FUNCTION_D2L, FunctionExprent.FUNCTION_D2F, FunctionExprent.FUNCTION_I2B,
+ FunctionExprent.FUNCTION_I2C,
+ FunctionExprent.FUNCTION_I2S};
+
+ private static final int[] func4 =
+ new int[]{FunctionExprent.FUNCTION_LCMP, FunctionExprent.FUNCTION_FCMPL, FunctionExprent.FUNCTION_FCMPG, FunctionExprent.FUNCTION_DCMPL,
+ FunctionExprent.FUNCTION_DCMPG};
+
+ private static final int[] func5 =
+ new int[]{IfExprent.IF_EQ, IfExprent.IF_NE, IfExprent.IF_LT, IfExprent.IF_GE, IfExprent.IF_GT, IfExprent.IF_LE};
+
+ private static final int[] func6 =
+ new int[]{IfExprent.IF_ICMPEQ, IfExprent.IF_ICMPNE, IfExprent.IF_ICMPLT, IfExprent.IF_ICMPGE, IfExprent.IF_ICMPGT, IfExprent.IF_ICMPLE,
+ IfExprent.IF_ACMPEQ, IfExprent.IF_ACMPNE};
+
+ private static final int[] func7 = new int[]{IfExprent.IF_NULL, IfExprent.IF_NONNULL};
+
+ private static final int[] func8 = new int[]{MonitorExprent.MONITOR_ENTER, MonitorExprent.MONITOR_EXIT};
+
+ private static final int[] arr_type =
+ new int[]{CodeConstants.TYPE_BOOLEAN, CodeConstants.TYPE_CHAR, CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_DOUBLE,
+ CodeConstants.TYPE_BYTE, CodeConstants.TYPE_SHORT, CodeConstants.TYPE_INT, CodeConstants.TYPE_LONG};
+
+ private static final int[] negifs =
+ new int[]{IfExprent.IF_NE, IfExprent.IF_EQ, IfExprent.IF_GE, IfExprent.IF_LT, IfExprent.IF_LE, IfExprent.IF_GT, IfExprent.IF_NONNULL,
+ IfExprent.IF_NULL, IfExprent.IF_ICMPNE, IfExprent.IF_ICMPEQ, IfExprent.IF_ICMPGE, IfExprent.IF_ICMPLT, IfExprent.IF_ICMPLE,
+ IfExprent.IF_ICMPGT, IfExprent.IF_ACMPNE,
+ IfExprent.IF_ACMPEQ};
+
+ private static final String[] typeNames = new String[]{"byte", "char", "double", "float", "int", "long", "short", "boolean",};
+
+ private VarProcessor varProcessor = (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR);
+
+ public void processStatement(RootStatement root, StructClass cl) {
+
+ FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();
+ DirectGraph dgraph = flatthelper.buildDirectGraph(root);
+
+ // try {
+ // DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr12_my.dot"));
+ // } catch (Exception ex) {
+ // ex.printStackTrace();
+ // }
+
+ // collect finally entry points
+ Set<String> setFinallyShortRangeEntryPoints = new HashSet<String>();
+ for (List<FinallyPathWrapper> lst : dgraph.mapShortRangeFinallyPaths.values()) {
+ for (FinallyPathWrapper finwrap : lst) {
+ setFinallyShortRangeEntryPoints.add(finwrap.entry);
+ }
+ }
+
+ Set<String> setFinallyLongRangeEntryPaths = new HashSet<String>();
+ for (List<FinallyPathWrapper> lst : dgraph.mapLongRangeFinallyPaths.values()) {
+ for (FinallyPathWrapper finwrap : lst) {
+ setFinallyLongRangeEntryPaths.add(finwrap.source + "##" + finwrap.entry);
+ }
+ }
+
+ Map<String, VarExprent> mapCatch = new HashMap<String, VarExprent>();
+ collectCatchVars(root, flatthelper, mapCatch);
+
+ Map<DirectNode, Map<String, PrimitiveExprsList>> mapData = new HashMap<DirectNode, Map<String, PrimitiveExprsList>>();
+
+ LinkedList<DirectNode> stack = new LinkedList<DirectNode>();
+ LinkedList<LinkedList<String>> stackEntryPoint = new LinkedList<LinkedList<String>>();
+
+ stack.add(dgraph.first);
+ stackEntryPoint.add(new LinkedList<String>());
+
+ Map<String, PrimitiveExprsList> map = new HashMap<String, PrimitiveExprsList>();
+ map.put(null, new PrimitiveExprsList());
+ mapData.put(dgraph.first, map);
+
+ while (!stack.isEmpty()) {
+
+ DirectNode node = stack.removeFirst();
+ LinkedList<String> entrypoints = stackEntryPoint.removeFirst();
+
+ PrimitiveExprsList data;
+ if (mapCatch.containsKey(node.id)) {
+ data = getExpressionData(mapCatch.get(node.id));
+ }
+ else {
+ data = mapData.get(node).get(buildEntryPointKey(entrypoints));
+ }
+
+ BasicBlockStatement block = node.block;
+ if (block != null) {
+ processBlock(block, data, cl);
+ block.setExprents(data.getLstExprents());
+ }
+
+ String currentEntrypoint = entrypoints.isEmpty() ? null : entrypoints.getLast();
+
+ for (DirectNode nd : node.succs) {
+
+ boolean isSuccessor = true;
+ if (currentEntrypoint != null && dgraph.mapLongRangeFinallyPaths.containsKey(node.id)) {
+ isSuccessor = false;
+ for (FinallyPathWrapper finwraplong : dgraph.mapLongRangeFinallyPaths.get(node.id)) {
+ if (finwraplong.source.equals(currentEntrypoint) && finwraplong.destination.equals(nd.id)) {
+ isSuccessor = true;
+ break;
+ }
+ }
+ }
+
+ if (isSuccessor) {
+
+ Map<String, PrimitiveExprsList> mapSucc = mapData.get(nd);
+ if (mapSucc == null) {
+ mapData.put(nd, mapSucc = new HashMap<String, PrimitiveExprsList>());
+ }
+
+ LinkedList<String> ndentrypoints = new LinkedList<String>(entrypoints);
+
+ if (setFinallyLongRangeEntryPaths.contains(node.id + "##" + nd.id)) {
+ ndentrypoints.addLast(node.id);
+ }
+ else if (!setFinallyShortRangeEntryPoints.contains(nd.id) && dgraph.mapLongRangeFinallyPaths.containsKey(node.id)) {
+ ndentrypoints.removeLast(); // currentEntrypoint should
+ // not be null at this point
+ }
+
+ // handling of entry point loops
+ int succ_entry_index = ndentrypoints.indexOf(nd.id);
+ if (succ_entry_index >=
+ 0) { // we are in a loop (e.g. continue in a finally block), drop all entry points in the list beginning with succ_entry_index
+ for (int elements_to_remove = ndentrypoints.size() - succ_entry_index; elements_to_remove > 0; elements_to_remove--) {
+ ndentrypoints.removeLast();
+ }
+ }
+
+ String ndentrykey = buildEntryPointKey(ndentrypoints);
+ if (!mapSucc.containsKey(ndentrykey)) {
+
+ mapSucc.put(ndentrykey, copyVarExprents(data.copyStack()));
+
+ stack.add(nd);
+ stackEntryPoint.add(ndentrypoints);
+ }
+ }
+ }
+ }
+
+ initStatementExprents(root);
+ }
+
+ // FIXME: Ugly code, to be rewritten. A tuple class is needed.
+ private static String buildEntryPointKey(LinkedList<String> entrypoints) {
+ if (entrypoints.isEmpty()) {
+ return null;
+ }
+ else {
+ StringBuilder buffer = new StringBuilder();
+ for (String point : entrypoints) {
+ buffer.append(point);
+ buffer.append(":");
+ }
+ return buffer.toString();
+ }
+ }
+
+ private static PrimitiveExprsList copyVarExprents(PrimitiveExprsList data) {
+ ExprentStack stack = data.getStack();
+ for (int i = 0; i < stack.size(); i++) {
+ stack.set(i, stack.get(i).copy());
+ }
+ return data;
+ }
+
+ private static void collectCatchVars(Statement stat, FlattenStatementsHelper flatthelper, Map<String, VarExprent> map) {
+
+ List<VarExprent> lst = null;
+
+ if (stat.type == Statement.TYPE_CATCHALL) {
+ CatchAllStatement catchall = (CatchAllStatement)stat;
+ if (!catchall.isFinally()) {
+ lst = catchall.getVars();
+ }
+ }
+ else if (stat.type == Statement.TYPE_TRYCATCH) {
+ lst = ((CatchStatement)stat).getVars();
+ }
+
+ if (lst != null) {
+ for (int i = 1; i < stat.getStats().size(); i++) {
+ map.put(flatthelper.getMapDestinationNodes().get(stat.getStats().get(i).id)[0], lst.get(i - 1));
+ }
+ }
+
+ for (Statement st : stat.getStats()) {
+ collectCatchVars(st, flatthelper, map);
+ }
+ }
+
+ private static void initStatementExprents(Statement stat) {
+ stat.initExprents();
+
+ for (Statement st : stat.getStats()) {
+ initStatementExprents(st);
+ }
+ }
+
+ public void processBlock(BasicBlockStatement stat, PrimitiveExprsList data, StructClass cl) {
+
+ ConstantPool pool = cl.getPool();
+ StructBootstrapMethodsAttribute bootstrap =
+ (StructBootstrapMethodsAttribute)cl.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS);
+
+ BasicBlock block = stat.getBlock();
+
+ ExprentStack stack = data.getStack();
+ List<Exprent> exprlist = data.getLstExprents();
+
+ InstructionSequence seq = block.getSeq();
+
+ for (int i = 0; i < seq.length(); i++) {
+
+ Instruction instr = seq.getInstr(i);
+
+ switch (instr.opcode) {
+ case opc_aconst_null:
+ pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_NULL, null));
+ break;
+ case opc_bipush:
+ case opc_sipush:
+ pushEx(stack, exprlist, new ConstExprent(instr.getOperand(0), true));
+ break;
+ case opc_lconst_0:
+ case opc_lconst_1:
+ pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_LONG, new Long(instr.opcode - opc_lconst_0)));
+ break;
+ case opc_fconst_0:
+ case opc_fconst_1:
+ case opc_fconst_2:
+ pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_FLOAT, new Float(instr.opcode - opc_fconst_0)));
+ break;
+ case opc_dconst_0:
+ case opc_dconst_1:
+ pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_DOUBLE, new Double(instr.opcode - opc_dconst_0)));
+ break;
+ case opc_ldc:
+ case opc_ldc_w:
+ case opc_ldc2_w:
+ PrimitiveConstant cn = pool.getPrimitiveConstant(instr.getOperand(0));
+ pushEx(stack, exprlist, new ConstExprent(consts[cn.type - CONSTANT_Integer], cn.value));
+ break;
+ case opc_iload:
+ case opc_lload:
+ case opc_fload:
+ case opc_dload:
+ case opc_aload:
+ pushEx(stack, exprlist, new VarExprent(instr.getOperand(0), vartypes[instr.opcode - opc_iload], varProcessor));
+ break;
+ case opc_iaload:
+ case opc_laload:
+ case opc_faload:
+ case opc_daload:
+ case opc_aaload:
+ case opc_baload:
+ case opc_caload:
+ case opc_saload:
+ Exprent index = stack.pop();
+ Exprent arr = stack.pop();
+
+ VarType vartype = null;
+ switch (instr.opcode) {
+ case opc_laload:
+ vartype = VarType.VARTYPE_LONG;
+ break;
+ case opc_daload:
+ vartype = VarType.VARTYPE_DOUBLE;
+ }
+ pushEx(stack, exprlist, new ArrayExprent(arr, index, arrtypes[instr.opcode - opc_iaload]), vartype);
+ break;
+ case opc_istore:
+ case opc_lstore:
+ case opc_fstore:
+ case opc_dstore:
+ case opc_astore:
+ Exprent top = stack.pop();
+ int varindex = instr.getOperand(0);
+ AssignmentExprent assign =
+ new AssignmentExprent(new VarExprent(varindex, vartypes[instr.opcode - opc_istore], varProcessor), top);
+ exprlist.add(assign);
+ break;
+ case opc_iastore:
+ case opc_lastore:
+ case opc_fastore:
+ case opc_dastore:
+ case opc_aastore:
+ case opc_bastore:
+ case opc_castore:
+ case opc_sastore:
+ Exprent value = stack.pop();
+ Exprent index_store = stack.pop();
+ Exprent arr_store = stack.pop();
+ AssignmentExprent arrassign =
+ new AssignmentExprent(new ArrayExprent(arr_store, index_store, arrtypes[instr.opcode - opc_iastore]), value);
+ exprlist.add(arrassign);
+ break;
+ case opc_iadd:
+ case opc_ladd:
+ case opc_fadd:
+ case opc_dadd:
+ case opc_isub:
+ case opc_lsub:
+ case opc_fsub:
+ case opc_dsub:
+ case opc_imul:
+ case opc_lmul:
+ case opc_fmul:
+ case opc_dmul:
+ case opc_idiv:
+ case opc_ldiv:
+ case opc_fdiv:
+ case opc_ddiv:
+ case opc_irem:
+ case opc_lrem:
+ case opc_frem:
+ case opc_drem:
+ pushEx(stack, exprlist, new FunctionExprent(func1[(instr.opcode - opc_iadd) / 4], stack));
+ break;
+ case opc_ishl:
+ case opc_lshl:
+ case opc_ishr:
+ case opc_lshr:
+ case opc_iushr:
+ case opc_lushr:
+ case opc_iand:
+ case opc_land:
+ case opc_ior:
+ case opc_lor:
+ case opc_ixor:
+ case opc_lxor:
+ pushEx(stack, exprlist, new FunctionExprent(func2[(instr.opcode - opc_ishl) / 2], stack));
+ break;
+ case opc_ineg:
+ case opc_lneg:
+ case opc_fneg:
+ case opc_dneg:
+ pushEx(stack, exprlist, new FunctionExprent(FunctionExprent.FUNCTION_NEG, stack));
+ break;
+ case opc_iinc:
+ VarExprent vevar = new VarExprent(instr.getOperand(0), VarType.VARTYPE_INT, varProcessor);
+ exprlist.add(new AssignmentExprent(vevar, new FunctionExprent(
+ instr.getOperand(1) < 0 ? FunctionExprent.FUNCTION_SUB : FunctionExprent.FUNCTION_ADD, Arrays
+ .asList(new Exprent[]{vevar.copy(), new ConstExprent(VarType.VARTYPE_INT, new Integer(Math.abs(instr.getOperand(1))))}))));
+ break;
+ case opc_i2l:
+ case opc_i2f:
+ case opc_i2d:
+ case opc_l2i:
+ case opc_l2f:
+ case opc_l2d:
+ case opc_f2i:
+ case opc_f2l:
+ case opc_f2d:
+ case opc_d2i:
+ case opc_d2l:
+ case opc_d2f:
+ case opc_i2b:
+ case opc_i2c:
+ case opc_i2s:
+ pushEx(stack, exprlist, new FunctionExprent(func3[instr.opcode - opc_i2l], stack));
+ break;
+ case opc_lcmp:
+ case opc_fcmpl:
+ case opc_fcmpg:
+ case opc_dcmpl:
+ case opc_dcmpg:
+ pushEx(stack, exprlist, new FunctionExprent(func4[instr.opcode - opc_lcmp], stack));
+ break;
+ case opc_ifeq:
+ case opc_ifne:
+ case opc_iflt:
+ case opc_ifge:
+ case opc_ifgt:
+ case opc_ifle:
+ exprlist.add(new IfExprent(negifs[func5[instr.opcode - opc_ifeq]], stack));
+ break;
+ case opc_if_icmpeq:
+ case opc_if_icmpne:
+ case opc_if_icmplt:
+ case opc_if_icmpge:
+ case opc_if_icmpgt:
+ case opc_if_icmple:
+ case opc_if_acmpeq:
+ case opc_if_acmpne:
+ exprlist.add(new IfExprent(negifs[func6[instr.opcode - opc_if_icmpeq]], stack));
+ break;
+ case opc_ifnull:
+ case opc_ifnonnull:
+ exprlist.add(new IfExprent(negifs[func7[instr.opcode - opc_ifnull]], stack));
+ break;
+ case opc_tableswitch:
+ case opc_lookupswitch:
+ exprlist.add(new SwitchExprent(stack.pop()));
+ break;
+ case opc_ireturn:
+ case opc_lreturn:
+ case opc_freturn:
+ case opc_dreturn:
+ case opc_areturn:
+ case opc_return:
+ case opc_athrow:
+ exprlist.add(new ExitExprent(instr.opcode == opc_athrow ? ExitExprent.EXIT_THROW : ExitExprent.EXIT_RETURN,
+ instr.opcode == opc_return ? null : stack.pop(),
+ instr.opcode == opc_athrow
+ ? null
+ : ((MethodDescriptor)DecompilerContext
+ .getProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR)).ret));
+ break;
+ case opc_monitorenter:
+ case opc_monitorexit:
+ exprlist.add(new MonitorExprent(func8[instr.opcode - opc_monitorenter], stack.pop()));
+ break;
+ case opc_checkcast:
+ case opc_instanceof:
+ stack.push(new ConstExprent(new VarType(pool.getPrimitiveConstant(instr.getOperand(0)).getString(), true), null));
+ case opc_arraylength:
+ pushEx(stack, exprlist, new FunctionExprent(mapConsts.get(instr.opcode).intValue(), stack));
+ break;
+ case opc_getstatic:
+ case opc_getfield:
+ pushEx(stack, exprlist,
+ new FieldExprent(pool.getLinkConstant(instr.getOperand(0)), instr.opcode == opc_getstatic ? null : stack.pop()));
+ break;
+ case opc_putstatic:
+ case opc_putfield:
+ Exprent valfield = stack.pop();
+ Exprent exprfield =
+ new FieldExprent(pool.getLinkConstant(instr.getOperand(0)), instr.opcode == opc_putstatic ? null : stack.pop());
+ exprlist.add(new AssignmentExprent(exprfield, valfield));
+ break;
+ case opc_invokevirtual:
+ case opc_invokespecial:
+ case opc_invokestatic:
+ case opc_invokeinterface:
+ case opc_invokedynamic:
+ if (instr.opcode != opc_invokedynamic || instr.bytecode_version >= CodeConstants.BYTECODE_JAVA_7) {
+
+ LinkConstant invoke_constant = pool.getLinkConstant(instr.getOperand(0));
+ int dynamic_invokation_type = -1;
+
+ if (instr.opcode == opc_invokedynamic && bootstrap != null) {
+ List<PooledConstant> bootstrap_arguments = bootstrap.getMethodArguments(invoke_constant.index1);
+ LinkConstant content_method_handle = (LinkConstant)bootstrap_arguments.get(1);
+
+ dynamic_invokation_type = content_method_handle.index1;
+ }
+
+ InvocationExprent exprinv = new InvocationExprent(instr.opcode, invoke_constant, stack, dynamic_invokation_type);
+ if (exprinv.getDescriptor().ret.type == CodeConstants.TYPE_VOID) {
+ exprlist.add(exprinv);
+ }
+ else {
+ pushEx(stack, exprlist, exprinv);
+ }
+ }
+ break;
+ case opc_new:
+ case opc_anewarray:
+ case opc_multianewarray:
+ int arrdims = (instr.opcode == opc_new) ? 0 : (instr.opcode == opc_anewarray) ? 1 : instr.getOperand(1);
+ VarType arrtype = new VarType(pool.getPrimitiveConstant(instr.getOperand(0)).getString(), true);
+ if (instr.opcode != opc_multianewarray) {
+ arrtype.arraydim += arrdims;
+ }
+ pushEx(stack, exprlist, new NewExprent(arrtype, stack, arrdims));
+ break;
+ case opc_newarray:
+ pushEx(stack, exprlist, new NewExprent(new VarType(arr_type[instr.getOperand(0) - 4], 1), stack, 1));
+ break;
+ case opc_dup:
+ pushEx(stack, exprlist, stack.getByOffset(-1).copy());
+ break;
+ case opc_dup_x1:
+ insertByOffsetEx(-2, stack, exprlist, -1);
+ break;
+ case opc_dup_x2:
+ if (stack.getByOffset(-2).getExprType().stack_size == 2) {
+ insertByOffsetEx(-2, stack, exprlist, -1);
+ }
+ else {
+ insertByOffsetEx(-3, stack, exprlist, -1);
+ }
+ break;
+ case opc_dup2:
+ if (stack.getByOffset(-1).getExprType().stack_size == 2) {
+ pushEx(stack, exprlist, stack.getByOffset(-1).copy());
+ }
+ else {
+ pushEx(stack, exprlist, stack.getByOffset(-2).copy());
+ pushEx(stack, exprlist, stack.getByOffset(-2).copy());
+ }
+ break;
+ case opc_dup2_x1:
+ if (stack.getByOffset(-1).getExprType().stack_size == 2) {
+ insertByOffsetEx(-2, stack, exprlist, -1);
+ }
+ else {
+ insertByOffsetEx(-3, stack, exprlist, -2);
+ insertByOffsetEx(-3, stack, exprlist, -1);
+ }
+ break;
+ case opc_dup2_x2:
+ if (stack.getByOffset(-1).getExprType().stack_size == 2) {
+ if (stack.getByOffset(-2).getExprType().stack_size == 2) {
+ insertByOffsetEx(-2, stack, exprlist, -1);
+ }
+ else {
+ insertByOffsetEx(-3, stack, exprlist, -1);
+ }
+ }
+ else {
+ if (stack.getByOffset(-3).getExprType().stack_size == 2) {
+ insertByOffsetEx(-3, stack, exprlist, -2);
+ insertByOffsetEx(-3, stack, exprlist, -1);
+ }
+ else {
+ insertByOffsetEx(-4, stack, exprlist, -2);
+ insertByOffsetEx(-4, stack, exprlist, -1);
+ }
+ }
+ break;
+ case opc_swap:
+ insertByOffsetEx(-2, stack, exprlist, -1);
+ stack.pop();
+ break;
+ case opc_pop:
+ case opc_pop2:
+ stack.pop();
+ }
+ }
+ }
+
+ private void pushEx(ExprentStack stack, List<Exprent> exprlist, Exprent exprent) {
+ pushEx(stack, exprlist, exprent, null);
+ }
+
+ private void pushEx(ExprentStack stack, List<Exprent> exprlist, Exprent exprent, VarType vartype) {
+ int varindex = VarExprent.STACK_BASE + stack.size();
+ VarExprent var = new VarExprent(varindex, vartype == null ? exprent.getExprType() : vartype, varProcessor);
+ var.setStack(true);
+
+ exprlist.add(new AssignmentExprent(var, exprent));
+ stack.push(var.copy());
+ }
+
+ private void insertByOffsetEx(int offset, ExprentStack stack, List<Exprent> exprlist, int copyoffset) {
+
+ int base = VarExprent.STACK_BASE + stack.size();
+
+ LinkedList<VarExprent> lst = new LinkedList<VarExprent>();
+
+ for (int i = -1; i >= offset; i--) {
+ Exprent varex = stack.pop();
+ VarExprent varnew = new VarExprent(base + i + 1, varex.getExprType(), varProcessor);
+ varnew.setStack(true);
+ exprlist.add(new AssignmentExprent(varnew, varex));
+ lst.add(0, (VarExprent)varnew.copy());
+ }
+
+ Exprent exprent = lst.get(lst.size() + copyoffset).copy();
+ VarExprent var = new VarExprent(base + offset, exprent.getExprType(), varProcessor);
+ var.setStack(true);
+ exprlist.add(new AssignmentExprent(var, exprent));
+ lst.add(0, (VarExprent)var.copy());
+
+ for (VarExprent expr : lst) {
+ stack.push(expr);
+ }
+ }
+
+ public static String getTypeName(VarType type) {
+ return getTypeName(type, true);
+ }
+
+ public static String getTypeName(VarType type, boolean getShort) {
+
+ int tp = type.type;
+ if (tp <= CodeConstants.TYPE_BOOLEAN) {
+ return typeNames[tp];
+ }
+ else if (tp == CodeConstants.TYPE_UNKNOWN) {
+ return UNKNOWN_TYPE_STRING; // INFO: should not occur
+ }
+ else if (tp == CodeConstants.TYPE_NULL) {
+ return NULL_TYPE_STRING; // INFO: should not occur
+ }
+ else if (tp == CodeConstants.TYPE_VOID) {
+ return "void";
+ }
+ else if (tp == CodeConstants.TYPE_OBJECT) {
+ String ret = buildJavaClassName(type.value);
+ if (getShort) {
+ ret = DecompilerContext.getImportCollector().getShortName(ret);
+ }
+
+ if (ret == null) {
+ // FIXME: a warning should be logged
+ ret = UNDEFINED_TYPE_STRING;
+ }
+ return ret;
+ }
+
+ throw new RuntimeException("invalid type");
+ }
+
+ public static String getCastTypeName(VarType type) {
+ return getCastTypeName(type, true);
+ }
+
+ public static String getCastTypeName(VarType type, boolean getShort) {
+ String s = getTypeName(type, getShort);
+ int dim = type.arraydim;
+ while (dim-- > 0) {
+ s += "[]";
+ }
+ return s;
+ }
+
+ public static PrimitiveExprsList getExpressionData(VarExprent var) {
+ PrimitiveExprsList prlst = new PrimitiveExprsList();
+ VarExprent vartmp = new VarExprent(VarExprent.STACK_BASE, var.getExprType(), var.getProcessor());
+ vartmp.setStack(true);
+
+ prlst.getLstExprents().add(new AssignmentExprent(vartmp, var.copy()));
+ prlst.getStack().push(vartmp.copy());
+ return prlst;
+ }
+
+ public static boolean endsWithSemikolon(Exprent expr) {
+ int type = expr.type;
+ return !(type == Exprent.EXPRENT_SWITCH ||
+ type == Exprent.EXPRENT_MONITOR ||
+ type == Exprent.EXPRENT_IF ||
+ (type == Exprent.EXPRENT_VAR && ((VarExprent)expr)
+ .isClassdef()));
+ }
+
+ public static String jmpWrapper(Statement stat, int indent, boolean semicolon) {
+ StringBuilder buf = new StringBuilder(stat.toJava(indent));
+
+ String new_line_separator = DecompilerContext.getNewLineSeparator();
+
+ List<StatEdge> lstSuccs = stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL);
+ if (lstSuccs.size() == 1) {
+ StatEdge edge = lstSuccs.get(0);
+ if (edge.getType() != StatEdge.TYPE_REGULAR && edge.explicit && edge.getDestination().type != Statement.TYPE_DUMMYEXIT) {
+ buf.append(InterpreterUtil.getIndentString(indent));
+
+ switch (edge.getType()) {
+ case StatEdge.TYPE_BREAK:
+ buf.append("break");
+ break;
+ case StatEdge.TYPE_CONTINUE:
+ buf.append("continue");
+ }
+
+ if (edge.labeled) {
+ buf.append(" label").append(edge.closure.id);
+ }
+ buf.append(";").append(new_line_separator);
+ }
+ }
+
+ if (buf.length() == 0 && semicolon) {
+ buf.append(InterpreterUtil.getIndentString(indent)).append(";").append(new_line_separator);
+ }
+
+ return buf.toString();
+ }
+
+ public static String buildJavaClassName(String name) {
+ String res = name.replace('/', '.');
+
+ if (res.contains("$")) { // attempt to invoke foreign member
+ // classes correctly
+ StructClass cl = DecompilerContext.getStructContext().getClass(name);
+ if (cl == null || !cl.isOwn()) {
+ res = res.replace('$', '.');
+ }
+ }
+
+ return res;
+ }
+
+ public static String listToJava(List<Exprent> lst, int indent) {
+ if (lst == null || lst.isEmpty()) {
+ return "";
+ }
+
+ String indstr = InterpreterUtil.getIndentString(indent);
+ String new_line_separator = DecompilerContext.getNewLineSeparator();
+
+ StringBuilder buf = new StringBuilder();
+
+ for (Exprent expr : lst) {
+ String content = expr.toJava(indent);
+ if (content.length() > 0) {
+ if (expr.type != Exprent.EXPRENT_VAR || !((VarExprent)expr).isClassdef()) {
+ buf.append(indstr);
+ }
+ buf.append(content);
+ if (expr.type == Exprent.EXPRENT_MONITOR && ((MonitorExprent)expr).getMontype() == MonitorExprent.MONITOR_ENTER) {
+ buf.append("{}"); // empty synchronized block
+ }
+ if (endsWithSemikolon(expr)) {
+ buf.append(";");
+ }
+ buf.append(new_line_separator);
+ }
+ }
+
+ return buf.toString();
+ }
+
+ public static ConstExprent getDefaultArrayValue(VarType arrtype) {
+
+ ConstExprent defaultval;
+ if (arrtype.type == CodeConstants.TYPE_OBJECT || arrtype.arraydim > 0) {
+ defaultval = new ConstExprent(VarType.VARTYPE_NULL, null);
+ }
+ else if (arrtype.type == CodeConstants.TYPE_FLOAT) {
+ defaultval = new ConstExprent(VarType.VARTYPE_FLOAT, new Float(0));
+ }
+ else if (arrtype.type == CodeConstants.TYPE_LONG) {
+ defaultval = new ConstExprent(VarType.VARTYPE_LONG, new Long(0));
+ }
+ else if (arrtype.type == CodeConstants.TYPE_DOUBLE) {
+ defaultval = new ConstExprent(VarType.VARTYPE_DOUBLE, new Double(0));
+ }
+ else { // integer types
+ defaultval = new ConstExprent(0, true);
+ }
+
+ return defaultval;
+ }
+
+ public static boolean getCastedExprent(Exprent exprent, VarType leftType, StringBuilder buffer, int indent, boolean castNull) {
+ return getCastedExprent(exprent, leftType, buffer, indent, castNull, false);
+ }
+
+ public static boolean getCastedExprent(Exprent exprent,
+ VarType leftType,
+ StringBuilder buffer,
+ int indent,
+ boolean castNull,
+ boolean castAlways) {
+
+ boolean ret = false;
+ VarType rightType = exprent.getExprType();
+
+ String res = exprent.toJava(indent);
+
+ boolean cast =
+ !leftType.isSuperset(rightType) && (rightType.equals(VarType.VARTYPE_OBJECT) || leftType.type != CodeConstants.TYPE_OBJECT);
+ cast |= castAlways;
+
+ if (!cast && castNull && rightType.type == CodeConstants.TYPE_NULL) {
+ // check for a nameless anonymous class
+ cast = !UNDEFINED_TYPE_STRING.equals(getTypeName(leftType));
+ }
+ if (!cast) {
+ cast = isIntConstant(exprent) && VarType.VARTYPE_INT.isStrictSuperset(leftType);
+ }
+
+ if (cast) {
+ if (exprent.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST)) {
+ res = "(" + res + ")";
+ }
+
+ res = "(" + getCastTypeName(leftType) + ")" + res;
+ ret = true;
+ }
+
+ buffer.append(res);
+
+ return ret;
+ }
+
+ private static boolean isIntConstant(Exprent exprent) {
+
+ if (exprent.type == Exprent.EXPRENT_CONST) {
+ ConstExprent cexpr = (ConstExprent)exprent;
+ switch (cexpr.getConsttype().type) {
+ case CodeConstants.TYPE_BYTE:
+ case CodeConstants.TYPE_BYTECHAR:
+ case CodeConstants.TYPE_SHORT:
+ case CodeConstants.TYPE_SHORTCHAR:
+ case CodeConstants.TYPE_INT:
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ExprentStack.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ExprentStack.java
new file mode 100644
index 000000000000..c4cded20428d
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ExprentStack.java
@@ -0,0 +1,45 @@
+/*
+ * 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.modules.decompiler;
+
+import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
+import org.jetbrains.java.decompiler.util.ListStack;
+
+public class ExprentStack extends ListStack<Exprent> {
+
+ public ExprentStack() {
+ }
+
+ public ExprentStack(ListStack<Exprent> list) {
+ super(list);
+ pointer = list.getPointer();
+ }
+
+ public Exprent push(Exprent item) {
+ super.push(item);
+
+ return item;
+ }
+
+ public Exprent pop() {
+
+ return this.remove(--pointer);
+ }
+
+ public ExprentStack clone() {
+ return new ExprentStack(this);
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java
new file mode 100644
index 000000000000..6198da1b21bf
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java
@@ -0,0 +1,1087 @@
+/*
+ * 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.modules.decompiler;
+
+import org.jetbrains.java.decompiler.code.*;
+import org.jetbrains.java.decompiler.code.cfg.BasicBlock;
+import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph;
+import org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG;
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
+import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
+import org.jetbrains.java.decompiler.modules.code.DeadCodeHelper;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;
+import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph;
+import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode;
+import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper;
+import org.jetbrains.java.decompiler.modules.decompiler.sforms.SSAConstructorSparseEx;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar;
+import org.jetbrains.java.decompiler.struct.StructMethod;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+public class FinallyProcessor {
+
+ private Map<Integer, Integer> finallyBlockIDs = new HashMap<Integer, Integer>();
+ private Map<Integer, Integer> catchallBlockIDs = new HashMap<Integer, Integer>();
+
+ private VarProcessor varprocessor;
+
+ public FinallyProcessor(VarProcessor varprocessor) {
+ this.varprocessor = varprocessor;
+ }
+
+ public boolean iterateGraph(StructMethod mt, RootStatement root, ControlFlowGraph graph) {
+ // return processStatement(mt, root, graph, root);
+ return processStatementEx(mt, root, graph);
+ }
+
+ private boolean processStatementEx(StructMethod mt, RootStatement root, ControlFlowGraph graph) {
+
+ int bytecode_version = mt.getClassStruct().getBytecodeVersion();
+
+ LinkedList<Statement> stack = new LinkedList<Statement>();
+ stack.add(root);
+
+ while (!stack.isEmpty()) {
+
+ Statement stat = stack.removeLast();
+
+ Statement parent = stat.getParent();
+ if (parent != null && parent.type == Statement.TYPE_CATCHALL &&
+ stat == parent.getFirst() && !parent.isCopied()) {
+
+ CatchAllStatement fin = (CatchAllStatement)parent;
+ BasicBlock head = fin.getBasichead().getBlock();
+ BasicBlock handler = fin.getHandler().getBasichead().getBlock();
+
+ if (catchallBlockIDs.containsKey(handler.id)) {
+ // do nothing
+ }
+ else if (finallyBlockIDs.containsKey(handler.id)) {
+
+ fin.setFinally(true);
+
+ Integer var = finallyBlockIDs.get(handler.id);
+ fin.setMonitor(var == null ? null : new VarExprent(var.intValue(), VarType.VARTYPE_INT, varprocessor));
+ }
+ else {
+
+ Record inf = getFinallyInformation(mt, root, fin);
+
+ if (inf == null) { // inconsistent finally
+ catchallBlockIDs.put(handler.id, null);
+ }
+ else {
+
+ if (DecompilerContext.getOption(IFernflowerPreferences.FINALLY_DEINLINE) && verifyFinallyEx(graph, fin, inf)) {
+ finallyBlockIDs.put(handler.id, null);
+ }
+ else {
+
+ int varindex = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER);
+ insertSemaphore(graph, getAllBasicBlocks(fin.getFirst()), head, handler, varindex, inf, bytecode_version);
+
+ finallyBlockIDs.put(handler.id, varindex);
+ }
+
+ DeadCodeHelper.removeDeadBlocks(graph); // e.g. multiple return blocks after a nested finally
+ DeadCodeHelper.removeEmptyBlocks(graph);
+ DeadCodeHelper.mergeBasicBlocks(graph);
+ }
+
+ return true;
+ }
+ }
+
+ stack.addAll(stat.getStats());
+ }
+
+ return false;
+ }
+
+
+ // private boolean processStatement(StructMethod mt, RootStatement root, ControlFlowGraph graph, Statement stat) {
+ //
+ // boolean res = false;
+ //
+ // for(int i=stat.getStats().size()-1;i>=0;i--) {
+ // if(processStatement(mt, root, graph, stat.getStats().get(i))) {
+ // return true;
+ // }
+ // }
+ //
+ //
+ // if(stat.type == Statement.TYPE_CATCHALL && !stat.isCopied()) {
+ //
+ // CatchAllStatement fin = (CatchAllStatement)stat;
+ // BasicBlock head = fin.getBasichead().getBlock();
+ // BasicBlock handler = fin.getHandler().getBasichead().getBlock();
+ //
+ // if(catchallBlockIDs.containsKey(handler.id)) {
+ // ; // do nothing
+ // }else if(finallyBlockIDs.containsKey(handler.id)) {
+ //
+ // fin.setFinally(true);
+ //
+ // Integer var = finallyBlockIDs.get(handler.id);
+ // fin.setMonitor(var==null?null:new VarExprent(var.intValue(), VarType.VARTYPE_INT, varprocessor));
+ //
+ // } else {
+ //
+ // Object[] inf = getFinallyInformation(mt, root, fin);
+ //
+ // if(inf == null) { // inconsistent finally
+ // catchallBlockIDs.put(handler.id, null);
+ // } else {
+ //
+ // if(DecompilerContext.getOption(IFernflowerPreferences.FINALLY_DEINLINE) && verifyFinallyEx(graph, fin, inf)) {
+ // finallyBlockIDs.put(handler.id, null);
+ // } else {
+ //
+ // int varindex = DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER);
+ // insertSemaphore(graph, getAllBasicBlocks(fin.getFirst()), head, handler, varindex, inf);
+ //
+ // finallyBlockIDs.put(handler.id, varindex);
+ // }
+ //
+ // DeadCodeHelper.removeEmptyBlocks(graph);
+ // DeadCodeHelper.mergeBasicBlocks(graph);
+ // }
+ //
+ // res = true;
+ // }
+ // }
+ //
+ // return res;
+ // }
+
+ private static class Record {
+ private final int firstCode;
+ private final Map<BasicBlock, Boolean> mapLast;
+
+ private Record(int firstCode, Map<BasicBlock, Boolean> mapLast) {
+ this.firstCode = firstCode;
+ this.mapLast = mapLast;
+ }
+ }
+
+
+ private static Record getFinallyInformation(StructMethod mt, RootStatement root, CatchAllStatement fstat) {
+
+ Map<BasicBlock, Boolean> mapLast = new HashMap<BasicBlock, Boolean>();
+
+ BasicBlockStatement firstBlockStatement = fstat.getHandler().getBasichead();
+ BasicBlock firstBasicBlock = firstBlockStatement.getBlock();
+ Instruction instrFirst = firstBasicBlock.getInstruction(0);
+
+ int firstcode = 0;
+
+ switch (instrFirst.opcode) {
+ case CodeConstants.opc_pop:
+ firstcode = 1;
+ break;
+ case CodeConstants.opc_astore:
+ firstcode = 2;
+ }
+
+ ExprProcessor proc = new ExprProcessor();
+ proc.processStatement(root, mt.getClassStruct());
+
+ SSAConstructorSparseEx ssa = new SSAConstructorSparseEx();
+ ssa.splitVariables(root, mt);
+
+ List<Exprent> lstExprents = firstBlockStatement.getExprents();
+
+ VarVersionPaar varpaar = new VarVersionPaar((VarExprent)((AssignmentExprent)lstExprents.get(firstcode == 2 ? 1 : 0)).getLeft());
+
+ FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();
+ DirectGraph dgraph = flatthelper.buildDirectGraph(root);
+
+ LinkedList<DirectNode> stack = new LinkedList<DirectNode>();
+ stack.add(dgraph.first);
+
+ Set<DirectNode> setVisited = new HashSet<DirectNode>();
+
+ while (!stack.isEmpty()) {
+
+ DirectNode node = stack.removeFirst();
+
+ if (setVisited.contains(node)) {
+ continue;
+ }
+ setVisited.add(node);
+
+ BasicBlockStatement blockStatement = null;
+ if (node.block != null) {
+ blockStatement = node.block;
+ }
+ else if (node.preds.size() == 1) {
+ blockStatement = node.preds.get(0).block;
+ }
+
+ boolean isTrueExit = true;
+
+ if (firstcode != 1) {
+
+ isTrueExit = false;
+
+ for (int i = 0; i < node.exprents.size(); i++) {
+ Exprent exprent = node.exprents.get(i);
+
+ if (firstcode == 0) {
+ List<Exprent> lst = exprent.getAllExprents();
+ lst.add(exprent);
+
+ boolean found = false;
+ for (Exprent expr : lst) {
+ if (expr.type == Exprent.EXPRENT_VAR && new VarVersionPaar((VarExprent)expr).equals(varpaar)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ found = false;
+ if (exprent.type == Exprent.EXPRENT_EXIT) {
+ ExitExprent exexpr = (ExitExprent)exprent;
+ if (exexpr.getExittype() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_VAR) {
+ found = true;
+ }
+ }
+
+ if (!found) {
+ return null;
+ }
+ else {
+ isTrueExit = true;
+ }
+ }
+ }
+ else if (firstcode == 2) {
+ // search for a load instruction
+ if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
+ AssignmentExprent assexpr = (AssignmentExprent)exprent;
+ if (assexpr.getRight().type == Exprent.EXPRENT_VAR &&
+ new VarVersionPaar((VarExprent)assexpr.getRight()).equals(varpaar)) {
+
+ Exprent next = null;
+ if (i == node.exprents.size() - 1) {
+ if (node.succs.size() == 1) {
+ DirectNode nd = node.succs.get(0);
+ if (!nd.exprents.isEmpty()) {
+ next = nd.exprents.get(0);
+ }
+ }
+ }
+ else {
+ next = node.exprents.get(i + 1);
+ }
+
+ boolean found = false;
+ if (next != null && next.type == Exprent.EXPRENT_EXIT) {
+ ExitExprent exexpr = (ExitExprent)next;
+ if (exexpr.getExittype() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_VAR
+ && assexpr.getLeft().equals(exexpr.getValue())) {
+ found = true;
+ }
+ }
+
+ if (!found) {
+ return null;
+ }
+ else {
+ isTrueExit = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // find finally exits
+ if (blockStatement != null && blockStatement.getBlock() != null) {
+ Statement handler = fstat.getHandler();
+ for (StatEdge edge : blockStatement.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL)) {
+ if (edge.getType() != StatEdge.TYPE_REGULAR && handler.containsStatement(blockStatement)
+ && !handler.containsStatement(edge.getDestination())) {
+ Boolean existingFlag = mapLast.get(blockStatement.getBlock());
+ // note: the dummy node is also processed!
+ if (existingFlag == null || !existingFlag) {
+ mapLast.put(blockStatement.getBlock(), isTrueExit);
+ break;
+ }
+ }
+ }
+ }
+
+ stack.addAll(node.succs);
+ }
+
+ // empty finally block?
+ if (fstat.getHandler().type == Statement.TYPE_BASICBLOCK) {
+
+ boolean isEmpty = false;
+ boolean isFirstLast = mapLast.containsKey(firstBasicBlock);
+ InstructionSequence seq = firstBasicBlock.getSeq();
+
+ switch (firstcode) {
+ case 0:
+ isEmpty = isFirstLast && seq.length() == 1;
+ break;
+ case 1:
+ isEmpty = seq.length() == 1;
+ break;
+ case 2:
+ isEmpty = isFirstLast ? seq.length() == 3 : seq.length() == 1;
+ }
+
+ if (isEmpty) {
+ firstcode = 3;
+ }
+ }
+
+ return new Record(firstcode, mapLast);
+ }
+
+ private static void insertSemaphore(ControlFlowGraph graph,
+ Set<BasicBlock> setTry,
+ BasicBlock head,
+ BasicBlock handler,
+ int var,
+ Record information,
+ int bytecode_version) {
+
+ Set<BasicBlock> setCopy = new HashSet<BasicBlock>(setTry);
+
+ int finallytype = information.firstCode;
+ Map<BasicBlock, Boolean> mapLast = information.mapLast;
+
+ // first and last statements
+ removeExceptionInstructionsEx(handler, 1, finallytype);
+ for (Entry<BasicBlock, Boolean> entry : mapLast.entrySet()) {
+ BasicBlock last = entry.getKey();
+
+ if (entry.getValue()) {
+ removeExceptionInstructionsEx(last, 2, finallytype);
+ graph.getFinallyExits().add(last);
+ }
+ }
+
+ // disable semaphore at statement exit points
+ for (BasicBlock block : setTry) {
+
+ List<BasicBlock> lstSucc = block.getSuccs();
+ for (BasicBlock dest : lstSucc) {
+
+ // break out
+ if (!setCopy.contains(dest) && dest != graph.getLast()) {
+ // disable semaphore
+ SimpleInstructionSequence seq = new SimpleInstructionSequence();
+
+ seq.addInstruction(ConstantsUtil
+ .getInstructionInstance(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version,
+ new int[]{0}), -1);
+ seq.addInstruction(ConstantsUtil
+ .getInstructionInstance(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version,
+ new int[]{var}), -1);
+
+ // build a separate block
+ BasicBlock newblock = new BasicBlock(++graph.last_id);
+ newblock.setSeq(seq);
+
+ // insert between block and dest
+ block.replaceSuccessor(dest, newblock);
+ newblock.addSuccessor(dest);
+ setCopy.add(newblock);
+ graph.getBlocks().addWithKey(newblock, newblock.id);
+
+ // exception ranges
+ // FIXME: special case synchronized
+
+ // copy exception edges and extend protected ranges
+ for (int j = 0; j < block.getSuccExceptions().size(); j++) {
+ BasicBlock hd = block.getSuccExceptions().get(j);
+ newblock.addSuccessorException(hd);
+
+ ExceptionRangeCFG range = graph.getExceptionRange(hd, block);
+ range.getProtectedRange().add(newblock);
+ }
+ }
+ }
+ }
+
+ // enable semaphor at the statement entrance
+ SimpleInstructionSequence seq = new SimpleInstructionSequence();
+ seq.addInstruction(
+ ConstantsUtil.getInstructionInstance(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{1}),
+ -1);
+ seq.addInstruction(
+ ConstantsUtil.getInstructionInstance(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}),
+ -1);
+
+ BasicBlock newhead = new BasicBlock(++graph.last_id);
+ newhead.setSeq(seq);
+
+ insertBlockBefore(graph, head, newhead);
+
+ // initialize semaphor with false
+ seq = new SimpleInstructionSequence();
+ seq.addInstruction(
+ ConstantsUtil.getInstructionInstance(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}),
+ -1);
+ seq.addInstruction(
+ ConstantsUtil.getInstructionInstance(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}),
+ -1);
+
+ BasicBlock newheadinit = new BasicBlock(++graph.last_id);
+ newheadinit.setSeq(seq);
+
+ insertBlockBefore(graph, newhead, newheadinit);
+
+ setCopy.add(newhead);
+ setCopy.add(newheadinit);
+
+ for (BasicBlock hd : new HashSet<BasicBlock>(newheadinit.getSuccExceptions())) {
+ ExceptionRangeCFG range = graph.getExceptionRange(hd, newheadinit);
+
+ if (setCopy.containsAll(range.getProtectedRange())) {
+ newheadinit.removeSuccessorException(hd);
+ range.getProtectedRange().remove(newheadinit);
+ }
+ }
+ }
+
+
+ private static void insertBlockBefore(ControlFlowGraph graph, BasicBlock oldblock, BasicBlock newblock) {
+
+ List<BasicBlock> lstTemp = new ArrayList<BasicBlock>();
+ lstTemp.addAll(oldblock.getPreds());
+ lstTemp.addAll(oldblock.getPredExceptions());
+
+ // replace predecessors
+ for (BasicBlock pred : lstTemp) {
+ pred.replaceSuccessor(oldblock, newblock);
+ }
+
+ // copy exception edges and extend protected ranges
+ for (BasicBlock hd : oldblock.getSuccExceptions()) {
+ newblock.addSuccessorException(hd);
+
+ ExceptionRangeCFG range = graph.getExceptionRange(hd, oldblock);
+ range.getProtectedRange().add(newblock);
+ }
+
+ // replace handler
+ for (ExceptionRangeCFG range : graph.getExceptions()) {
+ if (range.getHandler() == oldblock) {
+ range.setHandler(newblock);
+ }
+ }
+
+ newblock.addSuccessor(oldblock);
+ graph.getBlocks().addWithKey(newblock, newblock.id);
+ if (graph.getFirst() == oldblock) {
+ graph.setFirst(newblock);
+ }
+ }
+
+ private static HashSet<BasicBlock> getAllBasicBlocks(Statement stat) {
+
+ List<Statement> lst = new LinkedList<Statement>();
+ lst.add(stat);
+
+ int index = 0;
+ do {
+ Statement st = lst.get(index);
+
+ if (st.type == Statement.TYPE_BASICBLOCK) {
+ index++;
+ }
+ else {
+ lst.addAll(st.getStats());
+ lst.remove(index);
+ }
+ }
+ while (index < lst.size());
+
+ HashSet<BasicBlock> res = new HashSet<BasicBlock>();
+
+ for (Statement st : lst) {
+ res.add(((BasicBlockStatement)st).getBlock());
+ }
+
+ return res;
+ }
+
+
+ private boolean verifyFinallyEx(ControlFlowGraph graph, CatchAllStatement fstat, Record information) {
+
+ HashSet<BasicBlock> tryBlocks = getAllBasicBlocks(fstat.getFirst());
+ HashSet<BasicBlock> catchBlocks = getAllBasicBlocks(fstat.getHandler());
+
+ int finallytype = information.firstCode;
+ Map<BasicBlock, Boolean> mapLast = information.mapLast;
+
+ BasicBlock first = fstat.getHandler().getBasichead().getBlock();
+ boolean skippedFirst = false;
+
+ if (finallytype == 3) {
+ // empty finally
+ removeExceptionInstructionsEx(first, 3, finallytype);
+
+ if (mapLast.containsKey(first)) {
+ graph.getFinallyExits().add(first);
+ }
+
+ return true;
+ }
+ else {
+ if (first.getSeq().length() == 1 && finallytype > 0) {
+ BasicBlock firstsuc = first.getSuccs().get(0);
+ if (catchBlocks.contains(firstsuc)) {
+ first = firstsuc;
+ skippedFirst = true;
+ }
+ }
+ }
+
+ // identify start blocks
+ HashSet<BasicBlock> startBlocks = new HashSet<BasicBlock>();
+ for (BasicBlock block : tryBlocks) {
+ startBlocks.addAll(block.getSuccs());
+ }
+ // throw in the try body will point directly to the dummy exit
+ // so remove dummy exit
+ startBlocks.remove(graph.getLast());
+ startBlocks.removeAll(tryBlocks);
+
+ List<Area> lstAreas = new ArrayList<Area>();
+
+ for (BasicBlock start : startBlocks) {
+
+ Area arr = compareSubgraphsEx(graph, start, catchBlocks, first, finallytype, mapLast, skippedFirst);
+ if (arr == null) {
+ return false;
+ }
+
+ lstAreas.add(arr);
+ }
+
+ // try {
+ // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true);
+ // } catch(Exception ex){ex.printStackTrace();}
+
+ // delete areas
+ for (Area area : lstAreas) {
+ deleteArea(graph, area);
+ }
+
+ // try {
+ // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true);
+ // } catch(Exception ex){ex.printStackTrace();}
+
+ // INFO: empty basic blocks may remain in the graph!
+ for (Entry<BasicBlock, Boolean> entry : mapLast.entrySet()) {
+ BasicBlock last = entry.getKey();
+
+ if (entry.getValue()) {
+ removeExceptionInstructionsEx(last, 2, finallytype);
+ graph.getFinallyExits().add(last);
+ }
+ }
+
+ removeExceptionInstructionsEx(fstat.getHandler().getBasichead().getBlock(), 1, finallytype);
+
+ return true;
+ }
+
+ private static class Area {
+ private final BasicBlock start;
+ private final Set<BasicBlock> sample;
+ private final BasicBlock next;
+
+ private Area(BasicBlock start, Set<BasicBlock> sample, BasicBlock next) {
+ this.start = start;
+ this.sample = sample;
+ this.next = next;
+ }
+ }
+
+ private Area compareSubgraphsEx(ControlFlowGraph graph,
+ BasicBlock startSample,
+ HashSet<BasicBlock> catchBlocks,
+ BasicBlock startCatch,
+ int finallytype,
+ Map<BasicBlock, Boolean> mapLast,
+ boolean skippedFirst) {
+
+ class BlockStackEntry {
+ public BasicBlock blockCatch;
+ public BasicBlock blockSample;
+
+ // TODO: correct handling (merging) of multiple paths
+ public List<int[]> lstStoreVars;
+
+ public BlockStackEntry(BasicBlock blockCatch, BasicBlock blockSample, List<int[]> lstStoreVars) {
+ this.blockCatch = blockCatch;
+ this.blockSample = blockSample;
+ this.lstStoreVars = new ArrayList<int[]>(lstStoreVars);
+ }
+ }
+
+ List<BlockStackEntry> stack = new LinkedList<BlockStackEntry>();
+
+ Set<BasicBlock> setSample = new HashSet<BasicBlock>();
+
+ Map<String, BasicBlock[]> mapNext = new HashMap<String, BasicBlock[]>();
+
+ stack.add(new BlockStackEntry(startCatch, startSample, new ArrayList<int[]>()));
+
+ while (!stack.isEmpty()) {
+
+ BlockStackEntry entry = stack.remove(0);
+ BasicBlock blockCatch = entry.blockCatch;
+ BasicBlock blockSample = entry.blockSample;
+
+ boolean isFirstBlock = !skippedFirst && blockCatch == startCatch;
+ boolean isLastBlock = mapLast.containsKey(blockCatch);
+ boolean isTrueLastBlock = isLastBlock && mapLast.get(blockCatch);
+
+ if (!compareBasicBlocksEx(graph, blockCatch, blockSample, (isFirstBlock ? 1 : 0) | (isTrueLastBlock ? 2 : 0), finallytype,
+ entry.lstStoreVars)) {
+ return null;
+ }
+
+ if (blockSample.getSuccs().size() != blockCatch.getSuccs().size()) {
+ return null;
+ }
+
+ setSample.add(blockSample);
+
+ // direct successors
+ for (int i = 0; i < blockCatch.getSuccs().size(); i++) {
+ BasicBlock sucCatch = blockCatch.getSuccs().get(i);
+ BasicBlock sucSample = blockSample.getSuccs().get(i);
+
+ if (catchBlocks.contains(sucCatch) && !setSample.contains(sucSample)) {
+ stack.add(new BlockStackEntry(sucCatch, sucSample, entry.lstStoreVars));
+ }
+ }
+
+
+ // exception successors
+ if (isLastBlock && blockSample.getSeq().isEmpty()) {
+ // do nothing, blockSample will be removed anyway
+ }
+ else {
+ if (blockCatch.getSuccExceptions().size() == blockSample.getSuccExceptions().size()) {
+ for (int i = 0; i < blockCatch.getSuccExceptions().size(); i++) {
+ BasicBlock sucCatch = blockCatch.getSuccExceptions().get(i);
+ BasicBlock sucSample = blockSample.getSuccExceptions().get(i);
+
+ String excCatch = graph.getExceptionRange(sucCatch, blockCatch).getUniqueExceptionsString();
+ String excSample = graph.getExceptionRange(sucSample, blockSample).getUniqueExceptionsString();
+
+ // FIXME: compare handlers if possible
+ boolean equalexc = excCatch == null ? excSample == null : excCatch.equals(excSample);
+
+ if (equalexc) {
+ if (catchBlocks.contains(sucCatch) && !setSample.contains(sucSample)) {
+
+ List<int[]> lst = entry.lstStoreVars;
+
+ if (sucCatch.getSeq().length() > 0 && sucSample.getSeq().length() > 0) {
+ Instruction instrCatch = sucCatch.getSeq().getInstr(0);
+ Instruction instrSample = sucSample.getSeq().getInstr(0);
+
+ if (instrCatch.opcode == CodeConstants.opc_astore &&
+ instrSample.opcode == CodeConstants.opc_astore) {
+ lst = new ArrayList<int[]>(lst);
+ lst.add(new int[]{instrCatch.getOperand(0), instrSample.getOperand(0)});
+ }
+ }
+
+ stack.add(new BlockStackEntry(sucCatch, sucSample, lst));
+ }
+ }
+ else {
+ return null;
+ }
+ }
+ }
+ else {
+ return null;
+ }
+ }
+
+ if (isLastBlock) {
+ Set<BasicBlock> setSuccs = new HashSet<BasicBlock>(blockSample.getSuccs());
+ setSuccs.removeAll(setSample);
+
+ for (BlockStackEntry stackent : stack) {
+ setSuccs.remove(stackent.blockSample);
+ }
+
+ for (BasicBlock succ : setSuccs) {
+ if (graph.getLast() != succ) { // FIXME: why?
+ mapNext.put(blockSample.id + "#" + succ.id, new BasicBlock[]{blockSample, succ, isTrueLastBlock ? succ : null});
+ }
+ }
+ }
+ }
+
+ return new Area(startSample, setSample, getUniqueNext(graph, new HashSet<BasicBlock[]>(mapNext.values())));
+ }
+
+ private static BasicBlock getUniqueNext(ControlFlowGraph graph, Set<BasicBlock[]> setNext) {
+
+ // precondition: there is at most one true exit path in a finally statement
+
+ BasicBlock next = null;
+ boolean multiple = false;
+
+ for (BasicBlock[] arr : setNext) {
+
+ if (arr[2] != null) {
+ next = arr[1];
+ multiple = false;
+ break;
+ }
+ else {
+ if (next == null) {
+ next = arr[1];
+ }
+ else if (next != arr[1]) {
+ multiple = true;
+ }
+
+ if (arr[1].getPreds().size() == 1) {
+ next = arr[1];
+ }
+ }
+ }
+
+ if (multiple) { // TODO: generic solution
+ for (BasicBlock[] arr : setNext) {
+ BasicBlock block = arr[1];
+
+ if (block != next) {
+ if (InterpreterUtil.equalSets(next.getSuccs(), block.getSuccs())) {
+ InstructionSequence seqNext = next.getSeq();
+ InstructionSequence seqBlock = block.getSeq();
+
+ if (seqNext.length() == seqBlock.length()) {
+ for (int i = 0; i < seqNext.length(); i++) {
+ Instruction instrNext = seqNext.getInstr(i);
+ Instruction instrBlock = seqBlock.getInstr(i);
+
+ if (instrNext.opcode != instrBlock.opcode || instrNext.wide != instrBlock.wide
+ || instrNext.operandsCount() != instrBlock.operandsCount()) {
+ return null;
+ }
+
+ for (int j = 0; j < instrNext.getOperands().length; j++) {
+ if (instrNext.getOperand(j) != instrBlock.getOperand(j)) {
+ return null;
+ }
+ }
+ }
+ }
+ else {
+ return null;
+ }
+ }
+ else {
+ return null;
+ }
+ }
+ }
+
+ // try {
+ // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true);
+ // } catch(IOException ex) {
+ // ex.printStackTrace();
+ // }
+
+ for (BasicBlock[] arr : setNext) {
+ if (arr[1] != next) {
+ // FIXME: exception edge possible?
+ arr[0].removeSuccessor(arr[1]);
+ arr[0].addSuccessor(next);
+ }
+ }
+
+ DeadCodeHelper.removeDeadBlocks(graph);
+ }
+
+ return next;
+ }
+
+ private boolean compareBasicBlocksEx(ControlFlowGraph graph,
+ BasicBlock pattern,
+ BasicBlock sample,
+ int type,
+ int finallytype,
+ List<int[]> lstStoreVars) {
+
+ InstructionSequence seqPattern = pattern.getSeq();
+ InstructionSequence seqSample = sample.getSeq();
+
+ if (type != 0) {
+ seqPattern = seqPattern.clone();
+
+ if ((type & 1) > 0) { // first
+ if (finallytype > 0) {
+ seqPattern.removeInstruction(0);
+ }
+ }
+
+ if ((type & 2) > 0) { // last
+ if (finallytype == 0 || finallytype == 2) {
+ seqPattern.removeInstruction(seqPattern.length() - 1);
+ }
+
+ if (finallytype == 2) {
+ seqPattern.removeInstruction(seqPattern.length() - 1);
+ }
+ }
+ }
+
+ if (seqPattern.length() > seqSample.length()) {
+ return false;
+ }
+
+ for (int i = 0; i < seqPattern.length(); i++) {
+ Instruction instrPattern = seqPattern.getInstr(i);
+ Instruction instrSample = seqSample.getInstr(i);
+
+ // compare instructions with respect to jumps
+ if (!equalInstructions(instrPattern, instrSample, lstStoreVars)) {
+ return false;
+ }
+ }
+
+ if (seqPattern.length() < seqSample.length()) { // split in two blocks
+
+ SimpleInstructionSequence seq = new SimpleInstructionSequence();
+ for (int i = seqSample.length() - 1; i >= seqPattern.length(); i--) {
+ seq.addInstruction(0, seqSample.getInstr(i), -1);
+ seqSample.removeInstruction(i);
+ }
+
+ BasicBlock newblock = new BasicBlock(++graph.last_id);
+ newblock.setSeq(seq);
+
+ List<BasicBlock> lstTemp = new ArrayList<BasicBlock>();
+ lstTemp.addAll(sample.getSuccs());
+
+ // move successors
+ for (BasicBlock suc : lstTemp) {
+ sample.removeSuccessor(suc);
+ newblock.addSuccessor(suc);
+ }
+
+ sample.addSuccessor(newblock);
+
+ graph.getBlocks().addWithKey(newblock, newblock.id);
+
+ Set<BasicBlock> setFinallyExits = graph.getFinallyExits();
+ if (setFinallyExits.contains(sample)) {
+ setFinallyExits.remove(sample);
+ setFinallyExits.add(newblock);
+ }
+
+ // copy exception edges and extend protected ranges
+ for (int j = 0; j < sample.getSuccExceptions().size(); j++) {
+ BasicBlock hd = sample.getSuccExceptions().get(j);
+ newblock.addSuccessorException(hd);
+
+ ExceptionRangeCFG range = graph.getExceptionRange(hd, sample);
+ range.getProtectedRange().add(newblock);
+ }
+ }
+
+ return true;
+ }
+
+ public boolean equalInstructions(Instruction first, Instruction second, List<int[]> lstStoreVars) {
+ if (first.opcode != second.opcode || first.wide != second.wide
+ || first.operandsCount() != second.operandsCount()) {
+ return false;
+ }
+
+ if (first.group != CodeConstants.GROUP_JUMP && first.getOperands() != null) { // FIXME: switch comparison
+ for (int i = 0; i < first.getOperands().length; i++) {
+
+ int firstOp = first.getOperand(i);
+ int secondOp = second.getOperand(i);
+
+ if (firstOp != secondOp) {
+
+ // a-load/store instructions
+ if (first.opcode == CodeConstants.opc_aload || first.opcode == CodeConstants.opc_astore) {
+ for (int[] arr : lstStoreVars) {
+ if (arr[0] == firstOp && arr[1] == secondOp) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private static void deleteArea(ControlFlowGraph graph, Area area) {
+
+ BasicBlock start = area.start;
+ BasicBlock next = area.next;
+
+ if (start == next) {
+ return;
+ }
+
+ if (next == null) {
+ // dummy exit block
+ next = graph.getLast();
+ }
+
+ // collect common exception ranges of predecessors and successors
+ Set<BasicBlock> setCommonExceptionHandlers = new HashSet<BasicBlock>(next.getSuccExceptions());
+ for (BasicBlock pred : start.getPreds()) {
+ setCommonExceptionHandlers.retainAll(pred.getSuccExceptions());
+ }
+
+ boolean is_outside_range = false;
+
+ Set<BasicBlock> setPredecessors = new HashSet<BasicBlock>(start.getPreds());
+
+ // replace start with next
+ for (BasicBlock pred : setPredecessors) {
+ pred.replaceSuccessor(start, next);
+ }
+
+ Set<BasicBlock> setBlocks = area.sample;
+
+ Set<ExceptionRangeCFG> setCommonRemovedExceptionRanges = null;
+
+ // remove all the blocks inbetween
+ for (BasicBlock block : setBlocks) {
+
+ // artificial basic blocks (those resulted from splitting)
+ // can belong to more than one area
+ if (graph.getBlocks().containsKey(block.id)) {
+
+ if (!block.getSuccExceptions().containsAll(setCommonExceptionHandlers)) {
+ is_outside_range = true;
+ }
+
+ Set<ExceptionRangeCFG> setRemovedExceptionRanges = new HashSet<ExceptionRangeCFG>();
+ for (BasicBlock handler : block.getSuccExceptions()) {
+ setRemovedExceptionRanges.add(graph.getExceptionRange(handler, block));
+ }
+
+ if (setCommonRemovedExceptionRanges == null) {
+ setCommonRemovedExceptionRanges = setRemovedExceptionRanges;
+ }
+ else {
+ setCommonRemovedExceptionRanges.retainAll(setRemovedExceptionRanges);
+ }
+
+ // shift extern edges on splitted blocks
+ if (block.getSeq().isEmpty() && block.getSuccs().size() == 1) {
+ BasicBlock succs = block.getSuccs().get(0);
+ for (BasicBlock pred : new ArrayList<BasicBlock>(block.getPreds())) {
+ if (!setBlocks.contains(pred)) {
+ pred.replaceSuccessor(block, succs);
+ }
+ }
+
+ if (graph.getFirst() == block) {
+ graph.setFirst(succs);
+ }
+ }
+
+ graph.removeBlock(block);
+ }
+ }
+
+ if (is_outside_range) {
+
+ // new empty block
+ BasicBlock emptyblock = new BasicBlock(++graph.last_id);
+ emptyblock.setSeq(new SimpleInstructionSequence());
+ graph.getBlocks().addWithKey(emptyblock, emptyblock.id);
+
+ // add to ranges if necessary
+ if (setCommonRemovedExceptionRanges != null) {
+ for (ExceptionRangeCFG range : setCommonRemovedExceptionRanges) {
+ emptyblock.addSuccessorException(range.getHandler());
+ range.getProtectedRange().add(emptyblock);
+ }
+ }
+
+ // insert between predecessors and next
+ emptyblock.addSuccessor(next);
+ for (BasicBlock pred : setPredecessors) {
+ pred.replaceSuccessor(next, emptyblock);
+ }
+ }
+ }
+
+ private static void removeExceptionInstructionsEx(BasicBlock block, int blocktype, int finallytype) {
+
+ InstructionSequence seq = block.getSeq();
+
+ if (finallytype == 3) { // empty finally handler
+ for (int i = seq.length() - 1; i >= 0; i--) {
+ seq.removeInstruction(i);
+ }
+ }
+ else {
+ if ((blocktype & 1) > 0) { // first
+ if (finallytype == 2 || finallytype == 1) { // astore or pop
+ seq.removeInstruction(0);
+ }
+ }
+
+ if ((blocktype & 2) > 0) { // last
+ if (finallytype == 2 || finallytype == 0) {
+ seq.removeInstruction(seq.length() - 1);
+ }
+
+ if (finallytype == 2) { // astore
+ seq.removeInstruction(seq.length() - 1);
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/IdeaNotNullHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/IdeaNotNullHelper.java
new file mode 100644
index 000000000000..0f5141a7aa52
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/IdeaNotNullHelper.java
@@ -0,0 +1,336 @@
+/*
+ * 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.modules.decompiler;
+
+import org.jetbrains.java.decompiler.code.CodeConstants;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
+import org.jetbrains.java.decompiler.struct.StructMethod;
+import org.jetbrains.java.decompiler.struct.attr.StructAnnotationAttribute;
+import org.jetbrains.java.decompiler.struct.attr.StructAnnotationParameterAttribute;
+import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;
+import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
+import org.jetbrains.java.decompiler.util.VBStyleCollection;
+
+import java.util.List;
+
+public class IdeaNotNullHelper {
+
+
+ public static boolean removeHardcodedChecks(Statement root, StructMethod mt) {
+
+ boolean checks_removed = false;
+
+ // parameter @NotNull annotations
+ while (findAndRemoveParameterCheck(root, mt)) { // iterate until nothing found. Each invocation removes one parameter check.
+ checks_removed = true;
+ }
+
+ // method @NotNull annotation
+ while (findAndRemoveReturnCheck(root, mt)) { // iterate until nothing found. Each invocation handles one method exit check.
+ checks_removed = true;
+ }
+
+ return checks_removed;
+ }
+
+ private static boolean findAndRemoveParameterCheck(Statement stat, StructMethod mt) {
+
+ Statement st = stat.getFirst();
+ while (st.type == Statement.TYPE_SEQUENCE) {
+ st = st.getFirst();
+ }
+
+ if (st.type == Statement.TYPE_IF) {
+
+ IfStatement ifstat = (IfStatement)st;
+ Statement ifbranch = ifstat.getIfstat();
+
+ Exprent if_condition = ifstat.getHeadexprent().getCondition();
+
+ boolean is_notnull_check = false;
+
+ // TODO: FUNCTION_NE also possible if reversed order (in theory)
+ if (ifbranch != null &&
+ if_condition.type == Exprent.EXPRENT_FUNCTION &&
+ ((FunctionExprent)if_condition).getFunctype() == FunctionExprent.FUNCTION_EQ &&
+ ifbranch.type == Statement.TYPE_BASICBLOCK &&
+ ifbranch.getExprents().size() == 1 &&
+ ifbranch.getExprents().get(0).type == Exprent.EXPRENT_EXIT) {
+
+ FunctionExprent func = (FunctionExprent)if_condition;
+ Exprent first_param = func.getLstOperands().get(0);
+ Exprent second_param = func.getLstOperands().get(1);
+
+ if (second_param.type == Exprent.EXPRENT_CONST &&
+ second_param.getExprType().type == CodeConstants.TYPE_NULL) { // TODO: reversed parameter order
+ if (first_param.type == Exprent.EXPRENT_VAR) {
+ VarExprent var = (VarExprent)first_param;
+
+ boolean thisvar = !mt.hasModifier(CodeConstants.ACC_STATIC);
+
+ MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
+ VBStyleCollection<StructGeneralAttribute, String> attributes = mt.getAttributes();
+
+ // parameter annotations
+ StructAnnotationParameterAttribute param_annotations = (StructAnnotationParameterAttribute)attributes
+ .getWithKey(StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS);
+ if (param_annotations != null) {
+
+ List<List<AnnotationExprent>> param_annotations_lists = param_annotations.getParamAnnotations();
+ int method_param_number = md.params.length;
+
+ int index = thisvar ? 1 : 0;
+ for (int i = 0; i < method_param_number; i++) {
+
+ if (index == var.getIndex()) {
+ if (param_annotations_lists.size() >= method_param_number - i) {
+ int shift = method_param_number -
+ param_annotations_lists
+ .size(); // NOTE: workaround for compiler bug, count annotations starting with the last parameter
+
+ List<AnnotationExprent> annotations = param_annotations_lists.get(i - shift);
+
+ for (AnnotationExprent ann : annotations) {
+ if (ann.getClassname().equals("org/jetbrains/annotations/NotNull")) {
+ is_notnull_check = true;
+ }
+ }
+ }
+
+ break;
+ }
+
+ index += md.params[i].stack_size;
+ }
+ }
+ }
+ }
+ }
+
+ if (!is_notnull_check) {
+ return false;
+ }
+
+ removeParameterCheck(stat, mt);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ private static void removeParameterCheck(Statement stat, StructMethod mt) {
+
+ Statement st = stat.getFirst();
+ while (st.type == Statement.TYPE_SEQUENCE) {
+ st = st.getFirst();
+ }
+
+ IfStatement ifstat = (IfStatement)st;
+
+ if (ifstat.getElsestat() == null) { // if
+ // TODO:
+ }
+ else { // if - else
+
+ StatEdge ifedge = ifstat.getIfEdge();
+ StatEdge elseedge = ifstat.getElseEdge();
+
+ Statement ifbranch = ifstat.getIfstat();
+ Statement elsebranch = ifstat.getElsestat();
+
+ ifstat.getFirst().removeSuccessor(ifedge);
+ ifstat.getFirst().removeSuccessor(elseedge);
+
+ ifstat.getStats().removeWithKey(ifbranch.id);
+ ifstat.getStats().removeWithKey(elsebranch.id);
+
+ if (!ifbranch.getAllSuccessorEdges().isEmpty()) {
+ ifbranch.removeSuccessor(ifbranch.getAllSuccessorEdges().get(0));
+ }
+
+ ifstat.getParent().replaceStatement(ifstat, elsebranch);
+ ifstat.getParent().setAllParent();
+ }
+ }
+
+ private static boolean findAndRemoveReturnCheck(Statement stat, StructMethod mt) {
+
+ VBStyleCollection<StructGeneralAttribute, String> attributes = mt.getAttributes();
+
+ boolean is_notnull_check = false;
+
+ // method annotation, refers to the return value
+ StructAnnotationAttribute attr =
+ (StructAnnotationAttribute)attributes.getWithKey(StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS);
+ if (attr != null) {
+ List<AnnotationExprent> annotations = attr.getAnnotations();
+
+ for (AnnotationExprent ann : annotations) {
+ if (ann.getClassname().equals("org/jetbrains/annotations/NotNull")) {
+ is_notnull_check = true;
+ }
+ }
+ }
+
+ if (is_notnull_check) {
+ return removeReturnCheck(stat, mt);
+ }
+
+ return false;
+ }
+
+
+ private static boolean removeReturnCheck(Statement stat, StructMethod mt) {
+
+ Statement parent = stat.getParent();
+
+ if (parent != null && parent.type == Statement.TYPE_IF && stat.type == Statement.TYPE_BASICBLOCK && stat.getExprents().size() == 1) {
+ Exprent exprent = stat.getExprents().get(0);
+ if (exprent.type == Exprent.EXPRENT_EXIT) {
+ ExitExprent exit_exprent = (ExitExprent)exprent;
+ if (exit_exprent.getExittype() == ExitExprent.EXIT_RETURN) {
+ Exprent exprent_value = exit_exprent.getValue();
+ //if(exprent_value.type == Exprent.EXPRENT_VAR) {
+ // VarExprent var_value = (VarExprent)exprent_value;
+
+ IfStatement ifparent = (IfStatement)parent;
+ Exprent if_condition = ifparent.getHeadexprent().getCondition();
+
+ if (ifparent.getElsestat() == stat && if_condition.type == Exprent.EXPRENT_FUNCTION &&
+ ((FunctionExprent)if_condition).getFunctype() == FunctionExprent.FUNCTION_EQ) { // TODO: reversed order possible (in theory)
+
+ FunctionExprent func = (FunctionExprent)if_condition;
+ Exprent first_param = func.getLstOperands().get(0);
+ Exprent second_param = func.getLstOperands().get(1);
+
+ StatEdge ifedge = ifparent.getIfEdge();
+ StatEdge elseedge = ifparent.getElseEdge();
+
+ Statement ifbranch = ifparent.getIfstat();
+ Statement elsebranch = ifparent.getElsestat();
+
+ if (second_param.type == Exprent.EXPRENT_CONST &&
+ second_param.getExprType().type == CodeConstants.TYPE_NULL) { // TODO: reversed parameter order
+ //if(first_param.type == Exprent.EXPRENT_VAR && ((VarExprent)first_param).getIndex() == var_value.getIndex()) {
+ if (first_param.equals(exprent_value)) { // TODO: check for absence of side effects like method invocations etc.
+ if (ifbranch.type == Statement.TYPE_BASICBLOCK &&
+ ifbranch.getExprents().size() == 1 &&
+ // TODO: special check for IllegalStateException
+ ifbranch.getExprents().get(0).type == Exprent.EXPRENT_EXIT) {
+
+ ifparent.getFirst().removeSuccessor(ifedge);
+ ifparent.getFirst().removeSuccessor(elseedge);
+
+ ifparent.getStats().removeWithKey(ifbranch.id);
+ ifparent.getStats().removeWithKey(elsebranch.id);
+
+ if (!ifbranch.getAllSuccessorEdges().isEmpty()) {
+ ifbranch.removeSuccessor(ifbranch.getAllSuccessorEdges().get(0));
+ }
+
+ if (!ifparent.getFirst().getExprents().isEmpty()) {
+ elsebranch.getExprents().addAll(0, ifparent.getFirst().getExprents());
+ }
+
+ ifparent.getParent().replaceStatement(ifparent, elsebranch);
+ ifparent.getParent().setAllParent();
+
+ return true;
+ }
+ }
+ }
+ }
+ //}
+ }
+ }
+ }
+ else if (parent != null &&
+ parent.type == Statement.TYPE_SEQUENCE &&
+ stat.type == Statement.TYPE_BASICBLOCK &&
+ stat.getExprents().size() == 1) {
+ Exprent exprent = stat.getExprents().get(0);
+ if (exprent.type == Exprent.EXPRENT_EXIT) {
+ ExitExprent exit_exprent = (ExitExprent)exprent;
+ if (exit_exprent.getExittype() == ExitExprent.EXIT_RETURN) {
+ Exprent exprent_value = exit_exprent.getValue();
+
+ SequenceStatement sequence = (SequenceStatement)parent;
+ int sequence_stats_number = sequence.getStats().size();
+
+ if (sequence_stats_number > 1 &&
+ sequence.getStats().getLast() == stat &&
+ sequence.getStats().get(sequence_stats_number - 2).type == Statement.TYPE_IF) {
+
+ IfStatement ifstat = (IfStatement)sequence.getStats().get(sequence_stats_number - 2);
+ Exprent if_condition = ifstat.getHeadexprent().getCondition();
+
+ if (ifstat.iftype == IfStatement.IFTYPE_IF && if_condition.type == Exprent.EXPRENT_FUNCTION &&
+ ((FunctionExprent)if_condition).getFunctype() == FunctionExprent.FUNCTION_EQ) { // TODO: reversed order possible (in theory)
+
+ FunctionExprent func = (FunctionExprent)if_condition;
+ Exprent first_param = func.getLstOperands().get(0);
+ Exprent second_param = func.getLstOperands().get(1);
+
+ Statement ifbranch = ifstat.getIfstat();
+
+ if (second_param.type == Exprent.EXPRENT_CONST &&
+ second_param.getExprType().type == CodeConstants.TYPE_NULL) { // TODO: reversed parameter order
+ if (first_param.equals(exprent_value)) { // TODO: check for absence of side effects like method invocations etc.
+ if (ifbranch.type == Statement.TYPE_BASICBLOCK &&
+ ifbranch.getExprents().size() == 1 &&
+ // TODO: special check for IllegalStateException
+ ifbranch.getExprents().get(0).type == Exprent.EXPRENT_EXIT) {
+
+ ifstat.removeSuccessor(ifstat.getAllSuccessorEdges().get(0)); // remove 'else' edge
+
+ if (!ifstat.getFirst().getExprents().isEmpty()) {
+ stat.getExprents().addAll(0, ifstat.getFirst().getExprents());
+ }
+
+ for (StatEdge edge : ifstat.getAllPredecessorEdges()) {
+
+ ifstat.removePredecessor(edge);
+ edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, stat);
+ stat.addPredecessor(edge);
+ }
+
+ sequence.getStats().removeWithKey(ifstat.id);
+ sequence.setFirst(sequence.getStats().get(0));
+
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ for (Statement st : stat.getStats()) {
+ if (removeReturnCheck(st, mt)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java
new file mode 100644
index 000000000000..b1a955cfd528
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java
@@ -0,0 +1,750 @@
+/*
+ * 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.modules.decompiler;
+
+import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.IfExprent;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+
+public class IfHelper {
+
+
+ public static boolean mergeAllIfs(RootStatement root) {
+
+ boolean res = mergeAllIfsRec(root, new HashSet<Integer>());
+
+ if (res) {
+ SequenceHelper.condenseSequences(root);
+ }
+
+ return res;
+ }
+
+
+ private static boolean mergeAllIfsRec(Statement stat, HashSet<Integer> setReorderedIfs) {
+
+ boolean res = false;
+
+ if (stat.getExprents() == null) {
+
+ while (true) {
+
+ boolean changed = false;
+
+ for (Statement st : stat.getStats()) {
+
+ res |= mergeAllIfsRec(st, setReorderedIfs);
+
+ // collapse composed if's
+ if (changed = mergeIfs(st, setReorderedIfs)) {
+ break;
+ }
+ }
+
+ res |= changed;
+
+ if (!changed) {
+ break;
+ }
+ }
+ }
+
+ return res;
+ }
+
+
+ public static boolean mergeIfs(Statement statement, HashSet<Integer> setReorderedIfs) {
+
+ if (statement.type != Statement.TYPE_IF && statement.type != Statement.TYPE_SEQUENCE) {
+ return false;
+ }
+
+ boolean res = false;
+
+ while (true) {
+
+ boolean updated = false;
+
+ List<Statement> lst = new ArrayList<Statement>();
+ if (statement.type == Statement.TYPE_IF) {
+ lst.add(statement);
+ }
+ else {
+ lst.addAll(statement.getStats());
+ }
+
+ boolean stsingle = (lst.size() == 1);
+
+ for (Statement stat : lst) {
+
+ if (stat.type == Statement.TYPE_IF) {
+ IfNode rtnode = buildGraph((IfStatement)stat, stsingle);
+
+ if (rtnode == null) {
+ continue;
+ }
+
+ if (updated = collapseIfIf(rtnode)) {
+ break;
+ }
+
+ if (!setReorderedIfs.contains(stat.id)) {
+
+ if (updated = collapseIfElse(rtnode)) {
+ break;
+ }
+
+ if (updated = collapseElse(rtnode)) {
+ break;
+ }
+ }
+
+ if (updated = reorderIf((IfStatement)stat)) {
+ setReorderedIfs.add(stat.id);
+ break;
+ }
+ }
+ }
+
+ if (!updated) {
+ break;
+ }
+
+ res |= updated;
+ }
+
+ return res;
+ }
+
+ private static boolean collapseIfIf(IfNode rtnode) {
+
+ if (rtnode.edgetypes.get(0) == 0) {
+ IfNode ifbranch = rtnode.succs.get(0);
+ if (ifbranch.succs.size() == 2) {
+
+ // if-if branch
+ if (ifbranch.succs.get(1).value == rtnode.succs.get(1).value) {
+
+ IfStatement ifparent = (IfStatement)rtnode.value;
+ IfStatement ifchild = (IfStatement)ifbranch.value;
+ Statement ifinner = ifbranch.succs.get(0).value;
+
+ if (ifchild.getFirst().getExprents().isEmpty()) {
+
+ ifparent.getFirst().removeSuccessor(ifparent.getIfEdge());
+ ifchild.removeSuccessor(ifchild.getAllSuccessorEdges().get(0));
+ ifparent.getStats().removeWithKey(ifchild.id);
+
+ if (ifbranch.edgetypes.get(0).intValue() == 1) { // target null
+
+ ifparent.setIfstat(null);
+
+ StatEdge ifedge = ifchild.getIfEdge();
+
+ ifchild.getFirst().removeSuccessor(ifedge);
+ ifedge.setSource(ifparent.getFirst());
+
+ if (ifedge.closure == ifchild) {
+ ifedge.closure = null;
+ }
+ ifparent.getFirst().addSuccessor(ifedge);
+
+ ifparent.setIfEdge(ifedge);
+ }
+ else {
+ ifchild.getFirst().removeSuccessor(ifchild.getIfEdge());
+
+ StatEdge ifedge = new StatEdge(StatEdge.TYPE_REGULAR, ifparent.getFirst(), ifinner);
+ ifparent.getFirst().addSuccessor(ifedge);
+ ifparent.setIfEdge(ifedge);
+ ifparent.setIfstat(ifinner);
+
+ ifparent.getStats().addWithKey(ifinner, ifinner.id);
+ ifinner.setParent(ifparent);
+
+ if (!ifinner.getAllSuccessorEdges().isEmpty()) {
+ StatEdge edge = ifinner.getAllSuccessorEdges().get(0);
+ if (edge.closure == ifchild) {
+ edge.closure = null;
+ }
+ }
+ }
+
+ // merge if conditions
+ IfExprent statexpr = ifparent.getHeadexprent();
+
+ List<Exprent> lstOperands = new ArrayList<Exprent>();
+ lstOperands.add(statexpr.getCondition());
+ lstOperands.add(ifchild.getHeadexprent().getCondition());
+
+ statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_CADD, lstOperands));
+
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private static boolean collapseIfElse(IfNode rtnode) {
+
+ if (rtnode.edgetypes.get(0).intValue() == 0) {
+ IfNode ifbranch = rtnode.succs.get(0);
+ if (ifbranch.succs.size() == 2) {
+
+ // if-else branch
+ if (ifbranch.succs.get(0).value == rtnode.succs.get(1).value) {
+
+ IfStatement ifparent = (IfStatement)rtnode.value;
+ IfStatement ifchild = (IfStatement)ifbranch.value;
+
+ if (ifchild.getFirst().getExprents().isEmpty()) {
+
+ ifparent.getFirst().removeSuccessor(ifparent.getIfEdge());
+ ifchild.getFirst().removeSuccessor(ifchild.getIfEdge());
+ ifparent.getStats().removeWithKey(ifchild.id);
+
+ if (ifbranch.edgetypes.get(1).intValue() == 1 &&
+ ifbranch.edgetypes.get(0).intValue() == 1) { // target null
+
+ ifparent.setIfstat(null);
+
+ StatEdge ifedge = ifchild.getAllSuccessorEdges().get(0);
+
+ ifchild.removeSuccessor(ifedge);
+ ifedge.setSource(ifparent.getFirst());
+ ifparent.getFirst().addSuccessor(ifedge);
+
+ ifparent.setIfEdge(ifedge);
+ }
+ else {
+ throw new RuntimeException("inconsistent if structure!");
+ }
+
+ // merge if conditions
+ IfExprent statexpr = ifparent.getHeadexprent();
+
+ List<Exprent> lstOperands = new ArrayList<Exprent>();
+ lstOperands.add(statexpr.getCondition());
+ lstOperands.add(new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT,
+ Arrays.asList(new Exprent[]{ifchild.getHeadexprent().getCondition()})));
+ statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_CADD, lstOperands));
+
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+
+ private static boolean collapseElse(IfNode rtnode) {
+
+ if (rtnode.edgetypes.get(1).intValue() == 0) {
+ IfNode elsebranch = rtnode.succs.get(1);
+ if (elsebranch.succs.size() == 2) {
+
+ // else-if or else-else branch
+ int path = elsebranch.succs.get(1).value == rtnode.succs.get(0).value ? 2 :
+ (elsebranch.succs.get(0).value == rtnode.succs.get(0).value ? 1 : 0);
+
+ if (path > 0) {
+
+ IfStatement firstif = (IfStatement)rtnode.value;
+ IfStatement secondif = (IfStatement)elsebranch.value;
+ Statement parent = firstif.getParent();
+
+ if (secondif.getFirst().getExprents().isEmpty()) {
+
+ firstif.getFirst().removeSuccessor(firstif.getIfEdge());
+
+ // remove first if
+ firstif.removeAllSuccessors(secondif);
+
+ for (StatEdge edge : firstif.getAllPredecessorEdges()) {
+ if (!firstif.containsStatementStrict(edge.getSource())) {
+ firstif.removePredecessor(edge);
+ edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, secondif);
+ secondif.addPredecessor(edge);
+ }
+ }
+
+ parent.getStats().removeWithKey(firstif.id);
+ if (parent.getFirst() == firstif) {
+ parent.setFirst(secondif);
+ }
+
+ // merge if conditions
+ IfExprent statexpr = secondif.getHeadexprent();
+
+ List<Exprent> lstOperands = new ArrayList<Exprent>();
+ lstOperands.add(firstif.getHeadexprent().getCondition());
+
+ if (path == 2) {
+ lstOperands.set(0, new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT,
+ Arrays.asList(new Exprent[]{lstOperands.get(0)})));
+ }
+
+ lstOperands.add(statexpr.getCondition());
+
+ statexpr
+ .setCondition(new FunctionExprent(path == 1 ? FunctionExprent.FUNCTION_COR : FunctionExprent.FUNCTION_CADD, lstOperands));
+
+ if (secondif.getFirst().getExprents().isEmpty() &&
+ !firstif.getFirst().getExprents().isEmpty()) {
+
+ secondif.replaceStatement(secondif.getFirst(), firstif.getFirst());
+ }
+
+ return true;
+ }
+ }
+ }
+ else if (elsebranch.succs.size() == 1) {
+
+ if (elsebranch.succs.get(0).value == rtnode.succs.get(0).value) {
+
+ IfStatement firstif = (IfStatement)rtnode.value;
+ Statement second = elsebranch.value;
+
+ firstif.removeAllSuccessors(second);
+
+ for (StatEdge edge : second.getAllSuccessorEdges()) {
+ second.removeSuccessor(edge);
+ edge.setSource(firstif);
+ firstif.addSuccessor(edge);
+ }
+
+ StatEdge ifedge = firstif.getIfEdge();
+ firstif.getFirst().removeSuccessor(ifedge);
+
+ second.addSuccessor(new StatEdge(ifedge.getType(), second, ifedge.getDestination(), ifedge.closure));
+
+ StatEdge newifedge = new StatEdge(StatEdge.TYPE_REGULAR, firstif.getFirst(), second);
+ firstif.getFirst().addSuccessor(newifedge);
+ firstif.setIfstat(second);
+
+ firstif.getStats().addWithKey(second, second.id);
+ second.setParent(firstif);
+
+ firstif.getParent().getStats().removeWithKey(second.id);
+
+ // negate the if condition
+ IfExprent statexpr = firstif.getHeadexprent();
+ statexpr
+ .setCondition(new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, Arrays.asList(new Exprent[]{statexpr.getCondition()})));
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+
+ private static IfNode buildGraph(IfStatement stat, boolean stsingle) {
+
+ if (stat.iftype == IfStatement.IFTYPE_IFELSE) {
+ return null;
+ }
+
+ IfNode res = new IfNode(stat);
+
+ // if branch
+ Statement ifchild = stat.getIfstat();
+ if (ifchild == null) {
+ StatEdge edge = stat.getIfEdge();
+ res.addChild(new IfNode(edge.getDestination()), 1);
+ }
+ else {
+ IfNode ifnode = new IfNode(ifchild);
+ res.addChild(ifnode, 0);
+ if (ifchild.type == Statement.TYPE_IF && ((IfStatement)ifchild).iftype == IfStatement.IFTYPE_IF) {
+ IfStatement stat2 = (IfStatement)ifchild;
+ Statement ifchild2 = stat2.getIfstat();
+ if (ifchild2 == null) {
+ StatEdge edge = stat2.getIfEdge();
+ ifnode.addChild(new IfNode(edge.getDestination()), 1);
+ }
+ else {
+ ifnode.addChild(new IfNode(ifchild2), 0);
+ }
+ }
+
+ if (!ifchild.getAllSuccessorEdges().isEmpty()) {
+ ifnode.addChild(new IfNode(ifchild.getAllSuccessorEdges().get(0).getDestination()), 1);
+ }
+ }
+
+ // else branch
+ StatEdge edge = stat.getAllSuccessorEdges().get(0);
+ Statement elsechild = edge.getDestination();
+ IfNode elsenode = new IfNode(elsechild);
+
+ if (stsingle || edge.getType() != StatEdge.TYPE_REGULAR) {
+ res.addChild(elsenode, 1);
+ }
+ else {
+ res.addChild(elsenode, 0);
+ if (elsechild.type == Statement.TYPE_IF && ((IfStatement)elsechild).iftype == IfStatement.IFTYPE_IF) {
+ IfStatement stat2 = (IfStatement)elsechild;
+ Statement ifchild2 = stat2.getIfstat();
+ if (ifchild2 == null) {
+ elsenode.addChild(new IfNode(stat2.getIfEdge().getDestination()), 1);
+ }
+ else {
+ elsenode.addChild(new IfNode(ifchild2), 0);
+ }
+ }
+
+ if (!elsechild.getAllSuccessorEdges().isEmpty()) {
+ elsenode.addChild(new IfNode(elsechild.getAllSuccessorEdges().get(0).getDestination()), 1);
+ }
+ }
+
+ return res;
+ }
+
+
+ // FIXME: rewrite the entire method!!! keep in mind finally exits!!
+ private static boolean reorderIf(IfStatement ifstat) {
+
+ if (ifstat.iftype == IfStatement.IFTYPE_IFELSE) {
+ return false;
+ }
+
+ boolean ifdirect = false, elsedirect = false;
+ boolean noifstat = false, noelsestat = false;
+ boolean ifdirectpath = false, elsedirectpath = false;
+
+ Statement parent = ifstat.getParent();
+ Statement from = parent.type == Statement.TYPE_SEQUENCE ? parent : ifstat;
+
+ Statement next = getNextStatement(from);
+
+ if (ifstat.getIfstat() == null) {
+ noifstat = true;
+
+ if (ifstat.getIfEdge().getType() == StatEdge.TYPE_FINALLYEXIT) {
+ ifdirect = true;
+ }
+ else {
+ ifdirect = MergeHelper.isDirectPath(from, ifstat.getIfEdge().getDestination());
+ }
+ }
+ else {
+ List<StatEdge> lstSuccs = ifstat.getIfstat().getAllSuccessorEdges();
+ if (!lstSuccs.isEmpty() && lstSuccs.get(0).getType() == StatEdge.TYPE_FINALLYEXIT) {
+ ifdirect = true;
+ }
+ else {
+ ifdirect = hasDirectEndEdge(ifstat.getIfstat(), from);
+ }
+ }
+
+ Statement last = parent.type == Statement.TYPE_SEQUENCE ? parent.getStats().getLast() : ifstat;
+ noelsestat = (last == ifstat);
+
+ if (!last.getAllSuccessorEdges().isEmpty() && last.getAllSuccessorEdges().get(0).getType() == StatEdge.TYPE_FINALLYEXIT) {
+ elsedirect = true;
+ }
+ else {
+ elsedirect = hasDirectEndEdge(last, from);
+ }
+
+ if (!noelsestat && existsPath(ifstat, ifstat.getAllSuccessorEdges().get(0).getDestination())) {
+ return false;
+ }
+
+ if (!ifdirect && !noifstat) {
+ ifdirectpath = existsPath(ifstat, next);
+ }
+
+ if (!elsedirect && !noelsestat) {
+ SequenceStatement sequence = (SequenceStatement)parent;
+
+ for (int i = sequence.getStats().size() - 1; i >= 0; i--) {
+ Statement sttemp = sequence.getStats().get(i);
+ if (sttemp == ifstat) {
+ break;
+ }
+ else {
+ if (elsedirectpath = existsPath(sttemp, next)) {
+ break;
+ }
+ }
+ }
+ }
+
+ if ((ifdirect || ifdirectpath) && (elsedirect || elsedirectpath) && !noifstat && !noelsestat) { // if - then - else
+
+ SequenceStatement sequence = (SequenceStatement)parent;
+
+ // build and cut the new else statement
+ List<Statement> lst = new ArrayList<Statement>();
+ for (int i = sequence.getStats().size() - 1; i >= 0; i--) {
+ Statement sttemp = sequence.getStats().get(i);
+ if (sttemp == ifstat) {
+ break;
+ }
+ else {
+ lst.add(0, sttemp);
+ }
+ }
+
+ Statement stelse;
+ if (lst.size() == 1) {
+ stelse = lst.get(0);
+ }
+ else {
+ stelse = new SequenceStatement(lst);
+ stelse.setAllParent();
+ }
+
+ ifstat.removeSuccessor(ifstat.getAllSuccessorEdges().get(0));
+ for (Statement st : lst) {
+ sequence.getStats().removeWithKey(st.id);
+ }
+
+ StatEdge elseedge = new StatEdge(StatEdge.TYPE_REGULAR, ifstat.getFirst(), stelse);
+ ifstat.getFirst().addSuccessor(elseedge);
+ ifstat.setElsestat(stelse);
+ ifstat.setElseEdge(elseedge);
+
+ ifstat.getStats().addWithKey(stelse, stelse.id);
+ stelse.setParent(ifstat);
+
+ // if(next.type != Statement.TYPE_DUMMYEXIT && (ifdirect || elsedirect)) {
+ // StatEdge breakedge = new StatEdge(StatEdge.TYPE_BREAK, ifstat, next);
+ // sequence.addLabeledEdge(breakedge);
+ // ifstat.addSuccessor(breakedge);
+ // }
+
+ ifstat.iftype = IfStatement.IFTYPE_IFELSE;
+ }
+ else if (ifdirect && (!elsedirect || (noifstat && !noelsestat))) { // if - then
+
+ // negate the if condition
+ IfExprent statexpr = ifstat.getHeadexprent();
+ statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, Arrays.asList(new Exprent[]{statexpr.getCondition()})));
+
+ if (noelsestat) {
+ StatEdge ifedge = ifstat.getIfEdge();
+ StatEdge elseedge = ifstat.getAllSuccessorEdges().get(0);
+
+ if (noifstat) {
+ ifstat.getFirst().removeSuccessor(ifedge);
+ ifstat.removeSuccessor(elseedge);
+
+ ifedge.setSource(ifstat);
+ elseedge.setSource(ifstat.getFirst());
+
+ ifstat.addSuccessor(ifedge);
+ ifstat.getFirst().addSuccessor(elseedge);
+
+ ifstat.setIfEdge(elseedge);
+ }
+ else {
+ Statement ifbranch = ifstat.getIfstat();
+ SequenceStatement newseq = new SequenceStatement(Arrays.asList(ifstat, ifbranch));
+
+ ifstat.getFirst().removeSuccessor(ifedge);
+ ifstat.getStats().removeWithKey(ifbranch.id);
+ ifstat.setIfstat(null);
+
+ ifstat.removeSuccessor(elseedge);
+ elseedge.setSource(ifstat.getFirst());
+ ifstat.getFirst().addSuccessor(elseedge);
+
+ ifstat.setIfEdge(elseedge);
+
+ ifstat.getParent().replaceStatement(ifstat, newseq);
+ newseq.setAllParent();
+
+ ifstat.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, ifstat, ifbranch));
+ }
+ }
+ else {
+
+ SequenceStatement sequence = (SequenceStatement)parent;
+
+ // build and cut the new else statement
+ List<Statement> lst = new ArrayList<Statement>();
+ for (int i = sequence.getStats().size() - 1; i >= 0; i--) {
+ Statement sttemp = sequence.getStats().get(i);
+ if (sttemp == ifstat) {
+ break;
+ }
+ else {
+ lst.add(0, sttemp);
+ }
+ }
+
+ Statement stelse;
+ if (lst.size() == 1) {
+ stelse = lst.get(0);
+ }
+ else {
+ stelse = new SequenceStatement(lst);
+ stelse.setAllParent();
+ }
+
+ ifstat.removeSuccessor(ifstat.getAllSuccessorEdges().get(0));
+ for (Statement st : lst) {
+ sequence.getStats().removeWithKey(st.id);
+ }
+
+ if (noifstat) {
+ StatEdge ifedge = ifstat.getIfEdge();
+
+ ifstat.getFirst().removeSuccessor(ifedge);
+ ifedge.setSource(ifstat);
+ ifstat.addSuccessor(ifedge);
+ }
+ else {
+ Statement ifbranch = ifstat.getIfstat();
+
+ ifstat.getFirst().removeSuccessor(ifstat.getIfEdge());
+ ifstat.getStats().removeWithKey(ifbranch.id);
+
+ ifstat.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, ifstat, ifbranch));
+
+ sequence.getStats().addWithKey(ifbranch, ifbranch.id);
+ ifbranch.setParent(sequence);
+ }
+
+ StatEdge newifedge = new StatEdge(StatEdge.TYPE_REGULAR, ifstat.getFirst(), stelse);
+ ifstat.getFirst().addSuccessor(newifedge);
+ ifstat.setIfstat(stelse);
+ ifstat.setIfEdge(newifedge);
+
+ ifstat.getStats().addWithKey(stelse, stelse.id);
+ stelse.setParent(ifstat);
+ }
+ }
+ else {
+ return false;
+ }
+
+ return true;
+ }
+
+ private static boolean hasDirectEndEdge(Statement stat, Statement from) {
+
+ for (StatEdge edge : stat.getAllSuccessorEdges()) {
+ if (MergeHelper.isDirectPath(from, edge.getDestination())) {
+ return true;
+ }
+ }
+
+ if (stat.getExprents() == null) {
+ switch (stat.type) {
+ case Statement.TYPE_SEQUENCE:
+ return hasDirectEndEdge(stat.getStats().getLast(), from);
+ case Statement.TYPE_CATCHALL:
+ case Statement.TYPE_TRYCATCH:
+ for (Statement st : stat.getStats()) {
+ if (hasDirectEndEdge(st, from)) {
+ return true;
+ }
+ }
+ break;
+ case Statement.TYPE_IF:
+ IfStatement ifstat = (IfStatement)stat;
+ if (ifstat.iftype == IfStatement.IFTYPE_IFELSE) {
+ return hasDirectEndEdge(ifstat.getIfstat(), from) ||
+ hasDirectEndEdge(ifstat.getElsestat(), from);
+ }
+ break;
+ case Statement.TYPE_SYNCRONIZED:
+ return hasDirectEndEdge(stat.getStats().get(1), from);
+ case Statement.TYPE_SWITCH:
+ for (Statement st : stat.getStats()) {
+ if (hasDirectEndEdge(st, from)) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+
+ private static Statement getNextStatement(Statement stat) {
+
+ Statement parent = stat.getParent();
+ switch (parent.type) {
+ case Statement.TYPE_ROOT:
+ return ((RootStatement)parent).getDummyExit();
+ case Statement.TYPE_DO:
+ return parent;
+ case Statement.TYPE_SEQUENCE:
+ SequenceStatement sequence = (SequenceStatement)parent;
+ if (sequence.getStats().getLast() != stat) {
+ for (int i = sequence.getStats().size() - 1; i >= 0; i--) {
+ if (sequence.getStats().get(i) == stat) {
+ return sequence.getStats().get(i + 1);
+ }
+ }
+ }
+ }
+
+ return getNextStatement(parent);
+ }
+
+ private static boolean existsPath(Statement from, Statement to) {
+
+ for (StatEdge edge : to.getAllPredecessorEdges()) {
+ if (from.containsStatementStrict(edge.getSource())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static class IfNode {
+ public Statement value;
+
+ public List<IfNode> succs = new ArrayList<IfNode>();
+ public List<Integer> edgetypes = new ArrayList<Integer>();
+
+ public IfNode(Statement value) {
+ this.value = value;
+ }
+
+ public void addChild(IfNode child, int type) {
+ succs.add(child);
+ edgetypes.add(new Integer(type));
+ }
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java
new file mode 100644
index 000000000000..adeda024151b
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java
@@ -0,0 +1,220 @@
+/*
+ * 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.modules.decompiler;
+
+import org.jetbrains.java.decompiler.modules.decompiler.stats.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class InlineSingleBlockHelper {
+
+
+ public static boolean inlineSingleBlocks(RootStatement root) {
+
+ boolean res = inlineSingleBlocksRec(root);
+
+ if (res) {
+ SequenceHelper.condenseSequences(root);
+ }
+
+ return res;
+ }
+
+ private static boolean inlineSingleBlocksRec(Statement stat) {
+
+ boolean res = false;
+
+ for (Statement st : stat.getStats()) {
+ res |= inlineSingleBlocksRec(st);
+ }
+
+ if (stat.type == Statement.TYPE_SEQUENCE) {
+
+ SequenceStatement seq = (SequenceStatement)stat;
+ for (int i = 1; i < seq.getStats().size(); i++) {
+ if (isInlineable(seq, i)) {
+ inlineBlock(seq, i);
+ return true;
+ }
+ }
+ }
+
+ return res;
+ }
+
+ private static void inlineBlock(SequenceStatement seq, int index) {
+
+ Statement first = seq.getStats().get(index);
+ Statement pre = seq.getStats().get(index - 1);
+ pre.removeSuccessor(pre.getAllSuccessorEdges().get(0)); // single regular edge
+
+ StatEdge edge = first.getPredecessorEdges(StatEdge.TYPE_BREAK).get(0);
+ Statement source = edge.getSource();
+ Statement parent = source.getParent();
+ source.removeSuccessor(edge);
+
+ List<Statement> lst = new ArrayList<Statement>();
+ for (int i = seq.getStats().size() - 1; i >= index; i--) {
+ lst.add(0, seq.getStats().remove(i));
+ }
+
+ if (parent.type == Statement.TYPE_IF && ((IfStatement)parent).iftype == IfStatement.IFTYPE_IF &&
+ source == parent.getFirst()) {
+ IfStatement ifparent = (IfStatement)parent;
+ SequenceStatement block = new SequenceStatement(lst);
+ block.setAllParent();
+
+ StatEdge newedge = new StatEdge(StatEdge.TYPE_REGULAR, source, block);
+ source.addSuccessor(newedge);
+ ifparent.setIfEdge(newedge);
+ ifparent.setIfstat(block);
+
+ ifparent.getStats().addWithKey(block, block.id);
+ block.setParent(ifparent);
+ }
+ else {
+ lst.add(0, source);
+
+ SequenceStatement block = new SequenceStatement(lst);
+ block.setAllParent();
+
+ parent.replaceStatement(source, block);
+
+ // LabelHelper.lowContinueLabels not applicable because of forward continue edges
+ // LabelHelper.lowContinueLabels(block, new HashSet<StatEdge>());
+ // do it by hand
+ for (StatEdge prededge : block.getPredecessorEdges(StatEdge.TYPE_CONTINUE)) {
+
+ block.removePredecessor(prededge);
+ prededge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, prededge, source);
+ source.addPredecessor(prededge);
+
+ source.addLabeledEdge(prededge);
+ }
+
+
+ if (parent.type == Statement.TYPE_SWITCH) {
+ ((SwitchStatement)parent).sortEdgesAndNodes();
+ }
+
+ source.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, source, first));
+ }
+ }
+
+ private static boolean isInlineable(SequenceStatement seq, int index) {
+
+ Statement first = seq.getStats().get(index);
+ Statement pre = seq.getStats().get(index - 1);
+
+ if (pre.hasBasicSuccEdge()) {
+ return false;
+ }
+
+
+ List<StatEdge> lst = first.getPredecessorEdges(StatEdge.TYPE_BREAK);
+
+ if (lst.size() == 1) {
+ StatEdge edge = lst.get(0);
+
+ if (sameCatchRanges(edge)) {
+ if (edge.explicit) {
+ return true;
+ }
+ else {
+ for (int i = index; i < seq.getStats().size(); i++) {
+ if (!noExitLabels(seq.getStats().get(i), seq)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ // FIXME: count labels properly
+ }
+
+ return false;
+ }
+
+ private static boolean sameCatchRanges(StatEdge edge) {
+
+ Statement from = edge.getSource();
+ Statement to = edge.getDestination();
+
+ while (true) {
+
+ Statement parent = from.getParent();
+ if (parent.containsStatementStrict(to)) {
+ break;
+ }
+
+ if (parent.type == Statement.TYPE_TRYCATCH ||
+ parent.type == Statement.TYPE_CATCHALL) {
+ if (parent.getFirst() == from) {
+ return false;
+ }
+ }
+ else if (parent.type == Statement.TYPE_SYNCRONIZED) {
+ if (parent.getStats().get(1) == from) {
+ return false;
+ }
+ }
+
+ from = parent;
+ }
+
+ return true;
+ }
+
+ private static boolean noExitLabels(Statement block, Statement sequence) {
+
+ for (StatEdge edge : block.getAllSuccessorEdges()) {
+ if (edge.getType() != StatEdge.TYPE_REGULAR && edge.getDestination().type != Statement.TYPE_DUMMYEXIT) {
+ if (!sequence.containsStatementStrict(edge.getDestination())) {
+ return false;
+ }
+ }
+ }
+
+ for (Statement st : block.getStats()) {
+ if (!noExitLabels(st, sequence)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static boolean isBreakEdgeLabeled(Statement source, Statement closure) {
+
+ if (closure.type == Statement.TYPE_DO || closure.type == Statement.TYPE_SWITCH) {
+
+ Statement parent = source.getParent();
+
+ if (parent == closure) {
+ return false;
+ }
+ else {
+ return parent.type == Statement.TYPE_DO || parent.type == Statement.TYPE_SWITCH ||
+ isBreakEdgeLabeled(parent, closure);
+ }
+ }
+ else {
+ return true;
+ }
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java
new file mode 100644
index 000000000000..1f4b5eea0e22
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java
@@ -0,0 +1,506 @@
+/*
+ * 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.modules.decompiler;
+
+import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.*;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+
+public class LabelHelper {
+
+
+ public static void cleanUpEdges(RootStatement root) {
+
+ resetAllEdges(root);
+
+ removeNonImmediateEdges(root);
+
+ liftClosures(root);
+
+ lowContinueLabels(root, new HashSet<StatEdge>());
+
+ lowClosures(root);
+ }
+
+ public static void identifyLabels(RootStatement root) {
+
+ setExplicitEdges(root);
+
+ hideDefaultSwitchEdges(root);
+
+ processStatementLabel(root);
+
+ setRetEdgesUnlabeled(root);
+ }
+
+ private static void liftClosures(Statement stat) {
+
+ for (StatEdge edge : stat.getAllSuccessorEdges()) {
+ switch (edge.getType()) {
+ case StatEdge.TYPE_CONTINUE:
+ if (edge.getDestination() != edge.closure) {
+ edge.getDestination().addLabeledEdge(edge);
+ }
+ break;
+ case StatEdge.TYPE_BREAK:
+ Statement dest = edge.getDestination();
+ if (dest.type != Statement.TYPE_DUMMYEXIT) {
+ Statement parent = dest.getParent();
+
+ List<Statement> lst = new ArrayList<Statement>();
+ if (parent.type == Statement.TYPE_SEQUENCE) {
+ lst.addAll(parent.getStats());
+ }
+ else if (parent.type == Statement.TYPE_SWITCH) {
+ lst.addAll(((SwitchStatement)parent).getCaseStatements());
+ }
+
+ for (int i = 0; i < lst.size(); i++) {
+ if (lst.get(i) == dest) {
+ lst.get(i - 1).addLabeledEdge(edge);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ for (Statement st : stat.getStats()) {
+ liftClosures(st);
+ }
+ }
+
+ private static void removeNonImmediateEdges(Statement stat) {
+
+ for (Statement st : stat.getStats()) {
+ removeNonImmediateEdges(st);
+ }
+
+ if (!stat.hasBasicSuccEdge()) {
+ for (StatEdge edge : stat.getSuccessorEdges(StatEdge.TYPE_CONTINUE | StatEdge.TYPE_BREAK)) {
+ stat.removeSuccessor(edge);
+ }
+ }
+ }
+
+ public static void lowContinueLabels(Statement stat, HashSet<StatEdge> edges) {
+
+ boolean ok = (stat.type != Statement.TYPE_DO);
+ if (!ok) {
+ DoStatement dostat = (DoStatement)stat;
+ ok = dostat.getLooptype() == DoStatement.LOOP_DO ||
+ dostat.getLooptype() == DoStatement.LOOP_WHILE ||
+ (dostat.getLooptype() == DoStatement.LOOP_FOR && dostat.getIncExprent() == null);
+ }
+
+ if (ok) {
+ edges.addAll(stat.getPredecessorEdges(StatEdge.TYPE_CONTINUE));
+ }
+
+ if (ok && stat.type == Statement.TYPE_DO) {
+ for (StatEdge edge : edges) {
+ if (stat.containsStatementStrict(edge.getSource())) {
+
+ edge.getDestination().removePredecessor(edge);
+ edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, stat);
+ stat.addPredecessor(edge);
+
+ stat.addLabeledEdge(edge);
+ }
+ }
+ }
+
+ for (Statement st : stat.getStats()) {
+ if (st == stat.getFirst()) {
+ lowContinueLabels(st, edges);
+ }
+ else {
+ lowContinueLabels(st, new HashSet<StatEdge>());
+ }
+ }
+ }
+
+ public static void lowClosures(Statement stat) {
+
+ for (StatEdge edge : new ArrayList<StatEdge>(stat.getLabelEdges())) {
+
+ if (edge.getType() == StatEdge.TYPE_BREAK) { // FIXME: ?
+ for (Statement st : stat.getStats()) {
+ if (st.containsStatementStrict(edge.getSource())) {
+ if (MergeHelper.isDirectPath(st, edge.getDestination())) {
+ st.addLabeledEdge(edge);
+ }
+ }
+ }
+ }
+ }
+
+ for (Statement st : stat.getStats()) {
+ lowClosures(st);
+ }
+ }
+
+ private static void resetAllEdges(Statement stat) {
+
+ for (Statement st : stat.getStats()) {
+ resetAllEdges(st);
+ }
+
+ for (StatEdge edge : stat.getAllSuccessorEdges()) {
+ edge.explicit = true;
+ edge.labeled = true;
+ }
+ }
+
+ private static void setRetEdgesUnlabeled(RootStatement root) {
+ Statement exit = root.getDummyExit();
+ for (StatEdge edge : exit.getAllPredecessorEdges()) {
+ List<Exprent> lst = edge.getSource().getExprents();
+ if (edge.getType() == StatEdge.TYPE_FINALLYEXIT || (lst != null && !lst.isEmpty() &&
+ lst.get(lst.size() - 1).type == Exprent.EXPRENT_EXIT)) {
+ edge.labeled = false;
+ }
+ }
+ }
+
+ private static HashMap<Statement, List<StatEdge>> setExplicitEdges(Statement stat) {
+
+ HashMap<Statement, List<StatEdge>> mapEdges = new HashMap<Statement, List<StatEdge>>();
+
+ if (stat.getExprents() != null) {
+ return mapEdges;
+ }
+
+
+ switch (stat.type) {
+ case Statement.TYPE_TRYCATCH:
+ case Statement.TYPE_CATCHALL:
+
+ for (Statement st : stat.getStats()) {
+ HashMap<Statement, List<StatEdge>> mapEdges1 = setExplicitEdges(st);
+ processEdgesWithNext(st, mapEdges1, null);
+
+ if (stat.type == Statement.TYPE_TRYCATCH || st == stat.getFirst()) { // edges leaving a finally catch block are always explicit
+ // merge the maps
+ if (mapEdges1 != null) {
+ for (Entry<Statement, List<StatEdge>> entr : mapEdges1.entrySet()) {
+ if (mapEdges.containsKey(entr.getKey())) {
+ mapEdges.get(entr.getKey()).addAll(entr.getValue());
+ }
+ else {
+ mapEdges.put(entr.getKey(), entr.getValue());
+ }
+ }
+ }
+ }
+ }
+
+ break;
+ case Statement.TYPE_DO:
+ mapEdges = setExplicitEdges(stat.getFirst());
+ processEdgesWithNext(stat.getFirst(), mapEdges, stat);
+ break;
+ case Statement.TYPE_IF:
+ IfStatement ifstat = (IfStatement)stat;
+ // head statement is a basic block
+ if (ifstat.getIfstat() == null) { // empty if
+ processEdgesWithNext(ifstat.getFirst(), mapEdges, null);
+ }
+ else {
+ mapEdges = setExplicitEdges(ifstat.getIfstat());
+ processEdgesWithNext(ifstat.getIfstat(), mapEdges, null);
+
+ HashMap<Statement, List<StatEdge>> mapEdges1 = null;
+ if (ifstat.getElsestat() != null) {
+ mapEdges1 = setExplicitEdges(ifstat.getElsestat());
+ processEdgesWithNext(ifstat.getElsestat(), mapEdges1, null);
+ }
+
+ // merge the maps
+ if (mapEdges1 != null) {
+ for (Entry<Statement, List<StatEdge>> entr : mapEdges1.entrySet()) {
+ if (mapEdges.containsKey(entr.getKey())) {
+ mapEdges.get(entr.getKey()).addAll(entr.getValue());
+ }
+ else {
+ mapEdges.put(entr.getKey(), entr.getValue());
+ }
+ }
+ }
+ }
+ break;
+ case Statement.TYPE_ROOT:
+ mapEdges = setExplicitEdges(stat.getFirst());
+ processEdgesWithNext(stat.getFirst(), mapEdges, ((RootStatement)stat).getDummyExit());
+ break;
+ case Statement.TYPE_SEQUENCE:
+ int index = 0;
+ while (index < stat.getStats().size() - 1) {
+ Statement st = stat.getStats().get(index);
+ processEdgesWithNext(st, setExplicitEdges(st), stat.getStats().get(index + 1));
+ index++;
+ }
+
+ Statement st = stat.getStats().get(index);
+ mapEdges = setExplicitEdges(st);
+ processEdgesWithNext(st, mapEdges, null);
+ break;
+ case Statement.TYPE_SWITCH:
+ SwitchStatement swst = (SwitchStatement)stat;
+
+ for (int i = 0; i < swst.getCaseStatements().size() - 1; i++) {
+ Statement stt = swst.getCaseStatements().get(i);
+ Statement stnext = swst.getCaseStatements().get(i + 1);
+
+ if (stnext.getExprents() != null && stnext.getExprents().isEmpty()) {
+ stnext = stnext.getAllSuccessorEdges().get(0).getDestination();
+ }
+ processEdgesWithNext(stt, setExplicitEdges(stt), stnext);
+ }
+
+ int last = swst.getCaseStatements().size() - 1;
+ if (last >= 0) { // empty switch possible
+ Statement stlast = swst.getCaseStatements().get(last);
+ if (stlast.getExprents() != null && stlast.getExprents().isEmpty()) {
+ StatEdge edge = stlast.getAllSuccessorEdges().get(0);
+ mapEdges.put(edge.getDestination(), new ArrayList<StatEdge>(Arrays.asList(new StatEdge[]{edge})));
+ }
+ else {
+ mapEdges = setExplicitEdges(stlast);
+ processEdgesWithNext(stlast, mapEdges, null);
+ }
+ }
+
+ break;
+ case Statement.TYPE_SYNCRONIZED:
+ SynchronizedStatement synstat = (SynchronizedStatement)stat;
+
+ processEdgesWithNext(synstat.getFirst(), setExplicitEdges(stat.getFirst()), synstat.getBody()); // FIXME: basic block?
+ mapEdges = setExplicitEdges(synstat.getBody());
+ processEdgesWithNext(synstat.getBody(), mapEdges, null);
+ }
+
+
+ return mapEdges;
+ }
+
+ private static void processEdgesWithNext(Statement stat, HashMap<Statement, List<StatEdge>> mapEdges, Statement next) {
+
+ StatEdge statedge = null;
+
+ List<StatEdge> lstSuccs = stat.getAllSuccessorEdges();
+ if (!lstSuccs.isEmpty()) {
+ statedge = lstSuccs.get(0);
+
+ if (statedge.getDestination() == next) {
+ statedge.explicit = false;
+ statedge = null;
+ }
+ else {
+ next = statedge.getDestination();
+ }
+ }
+
+ // no next for a do statement
+ if (stat.type == Statement.TYPE_DO && ((DoStatement)stat).getLooptype() == DoStatement.LOOP_DO) {
+ next = null;
+ }
+
+ if (next == null) {
+ if (mapEdges.size() == 1) {
+ List<StatEdge> lstEdges = mapEdges.values().iterator().next();
+ if (lstEdges.size() > 1 && mapEdges.keySet().iterator().next().type != Statement.TYPE_DUMMYEXIT) {
+ StatEdge edge_example = lstEdges.get(0);
+
+ Statement closure = stat.getParent();
+ if (!closure.containsStatementStrict(edge_example.closure)) {
+ closure = edge_example.closure;
+ }
+
+ StatEdge newedge = new StatEdge(edge_example.getType(), stat, edge_example.getDestination(), closure);
+ stat.addSuccessor(newedge);
+
+ for (StatEdge edge : lstEdges) {
+ edge.explicit = false;
+ }
+
+ mapEdges.put(newedge.getDestination(), new ArrayList<StatEdge>(Arrays.asList(new StatEdge[]{newedge})));
+ }
+ }
+ }
+ else {
+
+ boolean implfound = false;
+
+ for (Entry<Statement, List<StatEdge>> entr : mapEdges.entrySet()) {
+ if (entr.getKey() == next) {
+ for (StatEdge edge : entr.getValue()) {
+ edge.explicit = false;
+ }
+ implfound = true;
+ break;
+ }
+ }
+
+ if (stat.getAllSuccessorEdges().isEmpty() && !implfound) {
+ List<StatEdge> lstEdges = null;
+ for (Entry<Statement, List<StatEdge>> entr : mapEdges.entrySet()) {
+ if (entr.getKey().type != Statement.TYPE_DUMMYEXIT &&
+ (lstEdges == null || entr.getValue().size() > lstEdges.size())) {
+ lstEdges = entr.getValue();
+ }
+ }
+
+ if (lstEdges != null && lstEdges.size() > 1) {
+ StatEdge edge_example = lstEdges.get(0);
+
+ Statement closure = stat.getParent();
+ if (!closure.containsStatementStrict(edge_example.closure)) {
+ closure = edge_example.closure;
+ }
+
+ StatEdge newedge = new StatEdge(edge_example.getType(), stat, edge_example.getDestination(), closure);
+ stat.addSuccessor(newedge);
+
+ for (StatEdge edge : lstEdges) {
+ edge.explicit = false;
+ }
+ }
+ }
+
+ mapEdges.clear();
+ }
+
+ if (statedge != null) {
+ mapEdges.put(statedge.getDestination(), new ArrayList<StatEdge>(Arrays.asList(new StatEdge[]{statedge})));
+ }
+ }
+
+ private static void hideDefaultSwitchEdges(Statement stat) {
+
+ if (stat.type == Statement.TYPE_SWITCH) {
+ SwitchStatement swst = (SwitchStatement)stat;
+
+ int last = swst.getCaseStatements().size() - 1;
+ if (last >= 0) { // empty switch possible
+ Statement stlast = swst.getCaseStatements().get(last);
+
+ if (stlast.getExprents() != null && stlast.getExprents().isEmpty()) {
+ if (!stlast.getAllSuccessorEdges().get(0).explicit) {
+ List<StatEdge> lstEdges = swst.getCaseEdges().get(last);
+ lstEdges.remove(swst.getDefault_edge());
+
+ if (lstEdges.isEmpty()) {
+ swst.getCaseStatements().remove(last);
+ swst.getCaseEdges().remove(last);
+ }
+ }
+ }
+ }
+ }
+
+ for (Statement st : stat.getStats()) {
+ hideDefaultSwitchEdges(st);
+ }
+ }
+
+ private static void processStatementLabel(Statement stat) {
+ processStatementLabel(stat, new HashSet<Statement>(), new HashSet<Statement>());
+ }
+
+ private static void processStatementLabel(Statement stat, Set<Statement> setBreak, Set<Statement> setContinue) {
+ if (stat.getExprents() == null) {
+ for (Statement st : stat.getStats()) {
+ processStatementLabel(st, setBreak, setContinue);
+ }
+
+ boolean shieldtype = (stat.type == Statement.TYPE_DO || stat.type == Statement.TYPE_SWITCH);
+
+ for (StatEdge edge : stat.getLabelEdges()) {
+ if (edge.explicit) {
+ if (shieldtype && ((edge.getType() == StatEdge.TYPE_BREAK && setBreak.contains(edge.getSource())) ||
+ (edge.getType() == StatEdge.TYPE_CONTINUE && setContinue.contains(edge.getSource())))) {
+ edge.labeled = false;
+ }
+ }
+ }
+
+ switch (stat.type) {
+ case Statement.TYPE_DO:
+ setContinue.clear();
+ case Statement.TYPE_SWITCH:
+ setBreak.clear();
+ }
+ }
+
+ setBreak.add(stat);
+ setContinue.add(stat);
+ }
+
+ public static void replaceContinueWithBreak(Statement stat) {
+
+ if (stat.type == Statement.TYPE_DO) {
+
+ List<StatEdge> lst = stat.getPredecessorEdges(StatEdge.TYPE_CONTINUE);
+
+ for (StatEdge edge : lst) {
+
+ if (edge.explicit) {
+ Statement minclosure = getMinContinueClosure(edge);
+
+ if (minclosure != edge.closure &&
+ !InlineSingleBlockHelper.isBreakEdgeLabeled(edge.getSource(), minclosure)) {
+ edge.getSource().changeEdgeType(Statement.DIRECTION_FORWARD, edge, StatEdge.TYPE_BREAK);
+ edge.labeled = false;
+ minclosure.addLabeledEdge(edge);
+ }
+ }
+ }
+ }
+
+ for (Statement st : stat.getStats()) {
+ replaceContinueWithBreak(st);
+ }
+ }
+
+ private static Statement getMinContinueClosure(StatEdge edge) {
+
+ Statement closure = edge.closure;
+ while (true) {
+
+ boolean found = false;
+
+ for (Statement st : closure.getStats()) {
+ if (st.containsStatementStrict(edge.getSource())) {
+ if (MergeHelper.isDirectPath(st, edge.getDestination())) {
+ closure = st;
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found) {
+ break;
+ }
+ }
+
+ return closure;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java
new file mode 100644
index 000000000000..d5c9e8f20aee
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java
@@ -0,0 +1,206 @@
+/*
+ * 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.modules.decompiler;
+
+import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Set;
+
+
+public class LoopExtractHelper {
+
+
+ public static boolean extractLoops(Statement root) {
+
+ boolean res = (extractLoopsRec(root) != 0);
+
+ if (res) {
+ SequenceHelper.condenseSequences(root);
+ }
+
+ return res;
+ }
+
+
+ private static int extractLoopsRec(Statement stat) {
+
+ boolean res = false;
+
+ while (true) {
+
+ boolean updated = false;
+
+ for (Statement st : stat.getStats()) {
+ int extr = extractLoopsRec(st);
+ res |= (extr != 0);
+
+ if (extr == 2) {
+ updated = true;
+ break;
+ }
+ }
+
+ if (!updated) {
+ break;
+ }
+ }
+
+ if (stat.type == Statement.TYPE_DO) {
+ if (extractLoop((DoStatement)stat)) {
+ return 2;
+ }
+ }
+
+ return res ? 1 : 0;
+ }
+
+
+ private static boolean extractLoop(DoStatement stat) {
+
+ if (stat.getLooptype() != DoStatement.LOOP_DO) {
+ return false;
+ }
+
+ for (StatEdge edge : stat.getLabelEdges()) {
+ if (edge.getType() != StatEdge.TYPE_CONTINUE && edge.getDestination().type != Statement.TYPE_DUMMYEXIT) {
+ return false;
+ }
+ }
+
+ if (!extractLastIf(stat)) {
+ return extractFirstIf(stat);
+ }
+ else {
+ return true;
+ }
+ }
+
+ private static boolean extractLastIf(DoStatement stat) {
+
+ // search for an if condition at the end of the loop
+ Statement last = stat.getFirst();
+ while (last.type == Statement.TYPE_SEQUENCE) {
+ last = last.getStats().getLast();
+ }
+
+ if (last.type == Statement.TYPE_IF) {
+ IfStatement lastif = (IfStatement)last;
+ if (lastif.iftype == IfStatement.IFTYPE_IF && lastif.getIfstat() != null) {
+ Statement ifstat = lastif.getIfstat();
+ StatEdge elseedge = lastif.getAllSuccessorEdges().get(0);
+
+ if (elseedge.getType() == StatEdge.TYPE_CONTINUE && elseedge.closure == stat) {
+
+ Set<Statement> set = stat.getNeighboursSet(StatEdge.TYPE_CONTINUE, Statement.DIRECTION_BACKWARD);
+ set.remove(last);
+
+ if (set.isEmpty()) { // no direct continues in a do{}while loop
+ if (isExternStatement(stat, ifstat, ifstat)) {
+ extractIfBlock(stat, lastif);
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private static boolean extractFirstIf(DoStatement stat) {
+
+ // search for an if condition at the entrance of the loop
+ Statement first = stat.getFirst();
+ while (first.type == Statement.TYPE_SEQUENCE) {
+ first = first.getFirst();
+ }
+
+ // found an if statement
+ if (first.type == Statement.TYPE_IF) {
+ IfStatement firstif = (IfStatement)first;
+
+ if (firstif.getFirst().getExprents().isEmpty()) {
+
+ if (firstif.iftype == IfStatement.IFTYPE_IF && firstif.getIfstat() != null) {
+ Statement ifstat = firstif.getIfstat();
+
+ if (isExternStatement(stat, ifstat, ifstat)) {
+ extractIfBlock(stat, firstif);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+
+ private static boolean isExternStatement(DoStatement loop, Statement block, Statement stat) {
+
+ for (StatEdge edge : stat.getAllSuccessorEdges()) {
+ if (loop.containsStatement(edge.getDestination()) &&
+ !block.containsStatement(edge.getDestination())) {
+ return false;
+ }
+ }
+
+ for (Statement st : stat.getStats()) {
+ if (!isExternStatement(loop, block, st)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+ private static void extractIfBlock(DoStatement loop, IfStatement ifstat) {
+
+ Statement target = ifstat.getIfstat();
+ StatEdge ifedge = ifstat.getIfEdge();
+
+ ifstat.setIfstat(null);
+ ifedge.getSource().changeEdgeType(Statement.DIRECTION_FORWARD, ifedge, StatEdge.TYPE_BREAK);
+ ifedge.closure = loop;
+ ifstat.getStats().removeWithKey(target.id);
+
+ loop.addLabeledEdge(ifedge);
+
+ SequenceStatement block = new SequenceStatement(Arrays.asList(loop, target));
+ loop.getParent().replaceStatement(loop, block);
+ block.setAllParent();
+
+ loop.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, loop, target));
+
+ for (StatEdge edge : new ArrayList<StatEdge>(block.getLabelEdges())) {
+ if (edge.getType() == StatEdge.TYPE_CONTINUE || edge == ifedge) {
+ loop.addLabeledEdge(edge);
+ }
+ }
+
+ for (StatEdge edge : block.getPredecessorEdges(StatEdge.TYPE_CONTINUE)) {
+ if (loop.containsStatementStrict(edge.getSource())) {
+ block.removePredecessor(edge);
+ edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, loop);
+ loop.addPredecessor(edge);
+ }
+ }
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/LowBreakHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/LowBreakHelper.java
new file mode 100644
index 000000000000..67e9248d0f83
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/LowBreakHelper.java
@@ -0,0 +1,208 @@
+/*
+ * 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.modules.decompiler;
+
+import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.SynchronizedStatement;
+
+import java.util.List;
+
+public class LowBreakHelper {
+
+ public static void lowBreakLabels(Statement root) {
+
+ lowBreakLabelsRec(root);
+
+ liftBreakLabels(root);
+ }
+
+ private static void lowBreakLabelsRec(Statement stat) {
+
+ while (true) {
+
+ boolean found = false;
+
+ for (StatEdge edge : stat.getLabelEdges()) {
+ if (edge.getType() == StatEdge.TYPE_BREAK) {
+ Statement minclosure = getMinClosure(stat, edge.getSource());
+ if (minclosure != stat) {
+ minclosure.addLabeledEdge(edge);
+ edge.labeled = isBreakEdgeLabeled(edge.getSource(), minclosure);
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found) {
+ break;
+ }
+ }
+
+ for (Statement st : stat.getStats()) {
+ lowBreakLabelsRec(st);
+ }
+ }
+
+ public static boolean isBreakEdgeLabeled(Statement source, Statement closure) {
+
+ if (closure.type == Statement.TYPE_DO || closure.type == Statement.TYPE_SWITCH) {
+
+ Statement parent = source.getParent();
+
+ if (parent == closure) {
+ return false;
+ }
+ else {
+ return isBreakEdgeLabeled(parent, closure) ||
+ (parent.type == Statement.TYPE_DO || parent.type == Statement.TYPE_SWITCH);
+ }
+ }
+ else {
+ return true;
+ }
+ }
+
+ public static Statement getMinClosure(Statement closure, Statement source) {
+
+ while (true) {
+
+ Statement newclosure = null;
+
+ switch (closure.type) {
+ case Statement.TYPE_SEQUENCE:
+ Statement last = closure.getStats().getLast();
+
+ if (isOkClosure(closure, source, last)) {
+ newclosure = last;
+ }
+ break;
+ case Statement.TYPE_IF:
+ IfStatement ifclosure = (IfStatement)closure;
+ if (isOkClosure(closure, source, ifclosure.getIfstat())) {
+ newclosure = ifclosure.getIfstat();
+ }
+ else if (isOkClosure(closure, source, ifclosure.getElsestat())) {
+ newclosure = ifclosure.getElsestat();
+ }
+ break;
+ case Statement.TYPE_TRYCATCH:
+ for (Statement st : closure.getStats()) {
+ if (isOkClosure(closure, source, st)) {
+ newclosure = st;
+ break;
+ }
+ }
+ break;
+ case Statement.TYPE_SYNCRONIZED:
+ Statement body = ((SynchronizedStatement)closure).getBody();
+
+ if (isOkClosure(closure, source, body)) {
+ newclosure = body;
+ }
+ }
+
+ if (newclosure == null) {
+ break;
+ }
+
+ closure = newclosure;
+ }
+
+ return closure;
+ }
+
+ private static boolean isOkClosure(Statement closure, Statement source, Statement stat) {
+
+ boolean ok = false;
+
+ if (stat != null && stat.containsStatementStrict(source)) {
+
+ List<StatEdge> lst = stat.getAllSuccessorEdges();
+
+ ok = lst.isEmpty();
+ if (!ok) {
+ StatEdge edge = lst.get(0);
+ ok = (edge.closure == closure && edge.getType() == StatEdge.TYPE_BREAK);
+ }
+ }
+
+ return ok;
+ }
+
+
+ private static void liftBreakLabels(Statement stat) {
+
+ for (Statement st : stat.getStats()) {
+ liftBreakLabels(st);
+ }
+
+
+ while (true) {
+
+ boolean found = false;
+
+ for (StatEdge edge : stat.getLabelEdges()) {
+ if (edge.explicit && edge.labeled && edge.getType() == StatEdge.TYPE_BREAK) {
+
+ Statement newclosure = getMaxBreakLift(stat, edge);
+
+ if (newclosure != null) {
+ newclosure.addLabeledEdge(edge);
+ edge.labeled = isBreakEdgeLabeled(edge.getSource(), newclosure);
+
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found) {
+ break;
+ }
+ }
+ }
+
+ private static Statement getMaxBreakLift(Statement stat, StatEdge edge) {
+
+ Statement closure = null;
+ Statement newclosure = stat;
+
+ while ((newclosure = getNextBreakLift(newclosure, edge)) != null) {
+ closure = newclosure;
+ }
+
+ return closure;
+ }
+
+ private static Statement getNextBreakLift(Statement stat, StatEdge edge) {
+
+ Statement closure = stat.getParent();
+
+ while (closure != null && !closure.containsStatementStrict(edge.getDestination())) {
+
+ boolean labeled = isBreakEdgeLabeled(edge.getSource(), closure);
+ if (closure.isLabeled() || !labeled) {
+ return closure;
+ }
+
+ closure = closure.getParent();
+ }
+
+ return null;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java
new file mode 100644
index 000000000000..71d3d7991287
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java
@@ -0,0 +1,421 @@
+/*
+ * 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.modules.decompiler;
+
+import org.jetbrains.java.decompiler.code.cfg.BasicBlock;
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.IfExprent;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.*;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+public class MergeHelper {
+
+ public static void enhanceLoops(Statement root) {
+
+ while (enhanceLoopsRec(root)) ;
+
+ SequenceHelper.condenseSequences(root);
+ }
+
+ private static boolean enhanceLoopsRec(Statement stat) {
+
+ boolean res = false;
+
+ for (Statement st : stat.getStats()) {
+ if (st.getExprents() == null) {
+ res |= enhanceLoopsRec(st);
+ }
+ }
+
+ if (stat.type == Statement.TYPE_DO) {
+ res |= enhanceLoop((DoStatement)stat);
+ }
+
+ return res;
+ }
+
+ private static boolean enhanceLoop(DoStatement stat) {
+
+ int oldloop = stat.getLooptype();
+
+ switch (oldloop) {
+ case DoStatement.LOOP_DO:
+
+ // identify a while loop
+ if (matchWhile(stat)) {
+ // identify a for loop - subtype of while
+ matchFor(stat);
+ }
+ else {
+ // identify a do{}while loop
+ matchDoWhile(stat);
+ }
+
+ break;
+ case DoStatement.LOOP_WHILE:
+ matchFor(stat);
+ }
+
+ return (stat.getLooptype() != oldloop);
+ }
+
+ private static boolean matchDoWhile(DoStatement stat) {
+
+ // search for an if condition at the end of the loop
+ Statement last = stat.getFirst();
+ while (last.type == Statement.TYPE_SEQUENCE) {
+ last = last.getStats().getLast();
+ }
+
+ if (last.type == Statement.TYPE_IF) {
+ IfStatement lastif = (IfStatement)last;
+ if (lastif.iftype == IfStatement.IFTYPE_IF && lastif.getIfstat() == null) {
+ StatEdge ifedge = lastif.getIfEdge();
+ StatEdge elseedge = lastif.getAllSuccessorEdges().get(0);
+
+ if ((ifedge.getType() == StatEdge.TYPE_BREAK && elseedge.getType() == StatEdge.TYPE_CONTINUE && elseedge.closure == stat
+ && isDirectPath(stat, ifedge.getDestination())) ||
+ (ifedge.getType() == StatEdge.TYPE_CONTINUE && elseedge.getType() == StatEdge.TYPE_BREAK && ifedge.closure == stat
+ && isDirectPath(stat, elseedge.getDestination()))) {
+
+ Set<Statement> set = stat.getNeighboursSet(StatEdge.TYPE_CONTINUE, Statement.DIRECTION_BACKWARD);
+ set.remove(last);
+
+ if (!set.isEmpty()) {
+ return false;
+ }
+
+
+ stat.setLooptype(DoStatement.LOOP_DOWHILE);
+
+ IfExprent ifexpr = (IfExprent)lastif.getHeadexprent().copy();
+ if (ifedge.getType() == StatEdge.TYPE_BREAK) {
+ ifexpr.negateIf();
+ }
+ stat.setConditionExprent(ifexpr.getCondition());
+ lastif.getFirst().removeSuccessor(ifedge);
+ lastif.removeSuccessor(elseedge);
+
+ // remove empty if
+ if (lastif.getFirst().getExprents().isEmpty()) {
+ removeLastEmptyStatement(stat, lastif);
+ }
+ else {
+ lastif.setExprents(lastif.getFirst().getExprents());
+
+ StatEdge newedge = new StatEdge(StatEdge.TYPE_CONTINUE, lastif, stat);
+ lastif.addSuccessor(newedge);
+ stat.addLabeledEdge(newedge);
+ }
+
+ if (stat.getAllSuccessorEdges().isEmpty()) {
+ StatEdge edge = elseedge.getType() == StatEdge.TYPE_CONTINUE ? ifedge : elseedge;
+
+ edge.setSource(stat);
+ if (edge.closure == stat) {
+ edge.closure = stat.getParent();
+ }
+ stat.addSuccessor(edge);
+ }
+
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private static boolean matchWhile(DoStatement stat) {
+
+ // search for an if condition at the entrance of the loop
+ Statement first = stat.getFirst();
+ while (first.type == Statement.TYPE_SEQUENCE) {
+ first = first.getFirst();
+ }
+
+ // found an if statement
+ if (first.type == Statement.TYPE_IF) {
+ IfStatement firstif = (IfStatement)first;
+
+ if (firstif.getFirst().getExprents().isEmpty()) {
+
+ if (firstif.iftype == IfStatement.IFTYPE_IF) {
+ if (firstif.getIfstat() == null) {
+ StatEdge ifedge = firstif.getIfEdge();
+ if (isDirectPath(stat, ifedge.getDestination())) {
+ // exit condition identified
+ stat.setLooptype(DoStatement.LOOP_WHILE);
+
+ // negate condition (while header)
+ IfExprent ifexpr = (IfExprent)firstif.getHeadexprent().copy();
+ ifexpr.negateIf();
+ stat.setConditionExprent(ifexpr.getCondition());
+
+ // remove edges
+ firstif.getFirst().removeSuccessor(ifedge);
+ firstif.removeSuccessor(firstif.getAllSuccessorEdges().get(0));
+
+ if (stat.getAllSuccessorEdges().isEmpty()) {
+ ifedge.setSource(stat);
+ if (ifedge.closure == stat) {
+ ifedge.closure = stat.getParent();
+ }
+ stat.addSuccessor(ifedge);
+ }
+
+ // remove empty if statement as it is now part of the loop
+ if (firstif == stat.getFirst()) {
+ BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock(
+ DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)));
+ bstat.setExprents(new ArrayList<Exprent>());
+ stat.replaceStatement(firstif, bstat);
+ }
+ else {
+ // precondition: sequence must contain more than one statement!
+ Statement sequence = firstif.getParent();
+ sequence.getStats().removeWithKey(firstif.id);
+ sequence.setFirst(sequence.getStats().get(0));
+ }
+
+ return true;
+ }
+ }
+ else {
+ StatEdge elseedge = firstif.getAllSuccessorEdges().get(0);
+ if (isDirectPath(stat, elseedge.getDestination())) {
+ // exit condition identified
+ stat.setLooptype(DoStatement.LOOP_WHILE);
+
+ // no need to negate the while condition
+ stat.setConditionExprent(((IfExprent)firstif.getHeadexprent().copy()).getCondition());
+
+ // remove edges
+ StatEdge ifedge = firstif.getIfEdge();
+ firstif.getFirst().removeSuccessor(ifedge);
+ firstif.removeSuccessor(elseedge);
+
+ if (stat.getAllSuccessorEdges().isEmpty()) {
+
+ elseedge.setSource(stat);
+ if (elseedge.closure == stat) {
+ elseedge.closure = stat.getParent();
+ }
+ stat.addSuccessor(elseedge);
+ }
+
+ if (firstif.getIfstat() == null) {
+ BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock(
+ DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)));
+ bstat.setExprents(new ArrayList<Exprent>());
+
+ ifedge.setSource(bstat);
+ bstat.addSuccessor(ifedge);
+
+ stat.replaceStatement(firstif, bstat);
+ }
+ else {
+ // replace the if statement with its content
+ first.getParent().replaceStatement(first, firstif.getIfstat());
+
+ // lift closures
+ for (StatEdge prededge : elseedge.getDestination().getPredecessorEdges(StatEdge.TYPE_BREAK)) {
+ if (stat.containsStatementStrict(prededge.closure)) {
+ stat.addLabeledEdge(prededge);
+ }
+ }
+
+ LabelHelper.lowClosures(stat);
+ }
+
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ public static boolean isDirectPath(Statement stat, Statement endstat) {
+
+ Set<Statement> setStat = stat.getNeighboursSet(Statement.STATEDGE_DIRECT_ALL, Statement.DIRECTION_FORWARD);
+ if (setStat.isEmpty()) {
+ Statement parent = stat.getParent();
+ if (parent == null) {
+ return false;
+ }
+ else {
+ switch (parent.type) {
+ case Statement.TYPE_ROOT:
+ return endstat.type == Statement.TYPE_DUMMYEXIT;
+ case Statement.TYPE_DO:
+ return (endstat == parent);
+ case Statement.TYPE_SWITCH:
+ SwitchStatement swst = (SwitchStatement)parent;
+ for (int i = 0; i < swst.getCaseStatements().size() - 1; i++) {
+ Statement stt = swst.getCaseStatements().get(i);
+ if (stt == stat) {
+ Statement stnext = swst.getCaseStatements().get(i + 1);
+
+ if (stnext.getExprents() != null && stnext.getExprents().isEmpty()) {
+ stnext = stnext.getAllSuccessorEdges().get(0).getDestination();
+ }
+ return (endstat == stnext);
+ }
+ }
+ default:
+ return isDirectPath(parent, endstat);
+ }
+ }
+ }
+ else {
+ return setStat.contains(endstat);
+ }
+ }
+
+ private static boolean matchFor(DoStatement stat) {
+
+ Exprent lastDoExprent = null, initDoExprent = null;
+ Statement lastData = null, preData = null;
+
+ // get last exprent
+ lastData = getLastDirectData(stat.getFirst());
+ if (lastData == null || lastData.getExprents().isEmpty()) {
+ return false;
+ }
+
+ List<Exprent> lstExpr = lastData.getExprents();
+ lastDoExprent = lstExpr.get(lstExpr.size() - 1);
+
+ boolean issingle = false;
+ if (lstExpr.size() == 1) { // single exprent
+ if (lastData.getAllPredecessorEdges().size() > 1) { // break edges
+ issingle = true;
+ }
+ }
+
+ boolean haslast = issingle || (lastDoExprent.type == Exprent.EXPRENT_ASSIGNMENT ||
+ lastDoExprent.type == Exprent.EXPRENT_FUNCTION);
+
+ if (!haslast) {
+ return false;
+ }
+
+ boolean hasinit = false;
+
+ // search for an initializing exprent
+ Statement current = stat;
+ while (true) {
+ Statement parent = current.getParent();
+ if (parent == null) {
+ break;
+ }
+
+ if (parent.type == Statement.TYPE_SEQUENCE) {
+ if (current == parent.getFirst()) {
+ current = parent;
+ }
+ else {
+ preData = current.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD).get(0);
+ preData = getLastDirectData(preData);
+ if (preData != null && !preData.getExprents().isEmpty()) {
+ initDoExprent = preData.getExprents().get(preData.getExprents().size() - 1);
+ if (initDoExprent.type == Exprent.EXPRENT_ASSIGNMENT) {
+ hasinit = true;
+ }
+ }
+ break;
+ }
+ }
+ else {
+ break;
+ }
+ }
+
+ if ((hasinit && haslast) || issingle) { // FIXME: issingle sufficient?
+
+ Set<Statement> set = stat.getNeighboursSet(StatEdge.TYPE_CONTINUE, Statement.DIRECTION_BACKWARD);
+ set.remove(lastData);
+
+ if (!set.isEmpty()) {
+ return false;
+ }
+
+ stat.setLooptype(DoStatement.LOOP_FOR);
+ if (hasinit) {
+ stat.setInitExprent(preData.getExprents().remove(preData.getExprents().size() - 1));
+ }
+ stat.setIncExprent(lastData.getExprents().remove(lastData.getExprents().size() - 1));
+ }
+
+ if (lastData.getExprents().isEmpty()) {
+ List<StatEdge> lst = lastData.getAllSuccessorEdges();
+ if (!lst.isEmpty()) {
+ lastData.removeSuccessor(lst.get(0));
+ }
+ removeLastEmptyStatement(stat, lastData);
+ }
+
+ return true;
+ }
+
+ private static void removeLastEmptyStatement(DoStatement dostat, Statement stat) {
+
+ if (stat == dostat.getFirst()) {
+ BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock(
+ DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)));
+ bstat.setExprents(new ArrayList<Exprent>());
+ dostat.replaceStatement(stat, bstat);
+ }
+ else {
+ for (StatEdge edge : stat.getAllPredecessorEdges()) {
+ edge.getSource().changeEdgeType(Statement.DIRECTION_FORWARD, edge, StatEdge.TYPE_CONTINUE);
+
+ stat.removePredecessor(edge);
+ edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, dostat);
+ dostat.addPredecessor(edge);
+
+ dostat.addLabeledEdge(edge);
+ }
+
+ // parent is a sequence statement
+ stat.getParent().getStats().removeWithKey(stat.id);
+ }
+ }
+
+ private static Statement getLastDirectData(Statement stat) {
+
+ if (stat.getExprents() != null) {
+ return stat;
+ }
+
+ switch (stat.type) {
+ case Statement.TYPE_SEQUENCE:
+ for (int i = stat.getStats().size() - 1; i >= 0; i--) {
+ Statement tmp = getLastDirectData(stat.getStats().get(i));
+ if (tmp == null || !tmp.getExprents().isEmpty()) {
+ return tmp;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java
new file mode 100644
index 000000000000..aacb61f339ab
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java
@@ -0,0 +1,154 @@
+/*
+ * 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.modules.decompiler;
+
+import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent;
+import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph;
+import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode;
+import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+
+public class PPandMMHelper {
+
+ private boolean exprentReplaced;
+
+ public boolean findPPandMM(RootStatement root) {
+
+ FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();
+ DirectGraph dgraph = flatthelper.buildDirectGraph(root);
+
+ LinkedList<DirectNode> stack = new LinkedList<DirectNode>();
+ stack.add(dgraph.first);
+
+ HashSet<DirectNode> setVisited = new HashSet<DirectNode>();
+
+ boolean res = false;
+
+ while (!stack.isEmpty()) {
+
+ DirectNode node = stack.removeFirst();
+
+ if (setVisited.contains(node)) {
+ continue;
+ }
+ setVisited.add(node);
+
+ res |= processExprentList(node.exprents);
+
+ stack.addAll(node.succs);
+ }
+
+ return res;
+ }
+
+ private boolean processExprentList(List<Exprent> lst) {
+
+ boolean result = false;
+
+ for (int i = 0; i < lst.size(); i++) {
+ Exprent exprent = lst.get(i);
+ exprentReplaced = false;
+
+ Exprent retexpr = processExprentRecursive(exprent);
+ if (retexpr != null) {
+ lst.set(i, retexpr);
+
+ result = true;
+ i--; // process the same exprent again
+ }
+
+ result |= exprentReplaced;
+ }
+
+ return result;
+ }
+
+ private Exprent processExprentRecursive(Exprent exprent) {
+
+ boolean replaced = true;
+ while (replaced) {
+ replaced = false;
+
+ for (Exprent expr : exprent.getAllExprents()) {
+ Exprent retexpr = processExprentRecursive(expr);
+ if (retexpr != null) {
+ exprent.replaceExprent(expr, retexpr);
+ replaced = true;
+ exprentReplaced = true;
+ break;
+ }
+ }
+ }
+
+ if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
+ AssignmentExprent as = (AssignmentExprent)exprent;
+
+ if (as.getRight().type == Exprent.EXPRENT_FUNCTION) {
+ FunctionExprent func = (FunctionExprent)as.getRight();
+
+ VarType midlayer = null;
+ if (func.getFunctype() >= FunctionExprent.FUNCTION_I2L &&
+ func.getFunctype() <= FunctionExprent.FUNCTION_I2S) {
+ midlayer = func.getSimpleCastType();
+ if (func.getLstOperands().get(0).type == Exprent.EXPRENT_FUNCTION) {
+ func = (FunctionExprent)func.getLstOperands().get(0);
+ }
+ else {
+ return null;
+ }
+ }
+
+ if (func.getFunctype() == FunctionExprent.FUNCTION_ADD ||
+ func.getFunctype() == FunctionExprent.FUNCTION_SUB) {
+ Exprent econd = func.getLstOperands().get(0);
+ Exprent econst = func.getLstOperands().get(1);
+
+ if (econst.type != Exprent.EXPRENT_CONST && econd.type == Exprent.EXPRENT_CONST &&
+ func.getFunctype() == FunctionExprent.FUNCTION_ADD) {
+ econd = econst;
+ econst = func.getLstOperands().get(0);
+ }
+
+ if (econst.type == Exprent.EXPRENT_CONST && ((ConstExprent)econst).hasValueOne()) {
+ Exprent left = as.getLeft();
+
+ VarType condtype = econd.getExprType();
+ if (left.equals(econd) && (midlayer == null || midlayer.equals(condtype))) {
+ FunctionExprent ret = new FunctionExprent(
+ func.getFunctype() == FunctionExprent.FUNCTION_ADD ? FunctionExprent.FUNCTION_PPI : FunctionExprent.FUNCTION_MMI,
+ Arrays.asList(new Exprent[]{econd}));
+ ret.setImplicitType(condtype);
+
+ exprentReplaced = true;
+ return ret;
+ }
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/PrimitiveExprsList.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/PrimitiveExprsList.java
new file mode 100644
index 000000000000..6dbc887e348b
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/PrimitiveExprsList.java
@@ -0,0 +1,49 @@
+/*
+ * 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.modules.decompiler;
+
+import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PrimitiveExprsList {
+
+ private List<Exprent> lstExprents = new ArrayList<Exprent>();
+
+ private ExprentStack stack = new ExprentStack();
+
+ public PrimitiveExprsList() {
+ }
+
+ public PrimitiveExprsList copyStack() {
+ PrimitiveExprsList prlst = new PrimitiveExprsList();
+ prlst.setStack(stack.clone());
+ return prlst;
+ }
+
+ public List<Exprent> getLstExprents() {
+ return lstExprents;
+ }
+
+ public ExprentStack getStack() {
+ return stack;
+ }
+
+ public void setStack(ExprentStack stack) {
+ this.stack = stack;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java
new file mode 100644
index 000000000000..5643f228e024
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java
@@ -0,0 +1,432 @@
+/*
+ * 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.modules.decompiler;
+
+import org.jetbrains.java.decompiler.code.CodeConstants;
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+public class SecondaryFunctionsHelper {
+
+ private static final int[] funcsnot = new int[]{
+ FunctionExprent.FUNCTION_NE,
+ FunctionExprent.FUNCTION_EQ,
+ FunctionExprent.FUNCTION_GE,
+ FunctionExprent.FUNCTION_LT,
+ FunctionExprent.FUNCTION_LE,
+ FunctionExprent.FUNCTION_GT,
+ FunctionExprent.FUNCTION_COR,
+ FunctionExprent.FUNCTION_CADD
+ };
+
+ private static final HashMap<Integer, Integer[]> mapNumComparisons = new HashMap<Integer, Integer[]>();
+
+ static {
+ mapNumComparisons.put(FunctionExprent.FUNCTION_EQ,
+ new Integer[]{FunctionExprent.FUNCTION_LT, FunctionExprent.FUNCTION_EQ, FunctionExprent.FUNCTION_GT});
+ mapNumComparisons.put(FunctionExprent.FUNCTION_NE,
+ new Integer[]{FunctionExprent.FUNCTION_GE, FunctionExprent.FUNCTION_NE, FunctionExprent.FUNCTION_LE});
+ mapNumComparisons.put(FunctionExprent.FUNCTION_GT, new Integer[]{FunctionExprent.FUNCTION_GE, FunctionExprent.FUNCTION_GT, null});
+ mapNumComparisons.put(FunctionExprent.FUNCTION_GE, new Integer[]{null, FunctionExprent.FUNCTION_GE, FunctionExprent.FUNCTION_GT});
+ mapNumComparisons.put(FunctionExprent.FUNCTION_LT, new Integer[]{null, FunctionExprent.FUNCTION_LT, FunctionExprent.FUNCTION_LE});
+ mapNumComparisons.put(FunctionExprent.FUNCTION_LE, new Integer[]{FunctionExprent.FUNCTION_LT, FunctionExprent.FUNCTION_LE, null});
+ }
+
+
+ public static boolean identifySecondaryFunctions(Statement stat) {
+
+ if (stat.getExprents() == null) {
+ // if(){;}else{...} -> if(!){...}
+ if (stat.type == Statement.TYPE_IF) {
+ IfStatement ifelsestat = (IfStatement)stat;
+ Statement ifstat = ifelsestat.getIfstat();
+
+ if (ifelsestat.iftype == IfStatement.IFTYPE_IFELSE && ifstat.getExprents() != null &&
+ ifstat.getExprents().isEmpty() && (ifstat.getAllSuccessorEdges().isEmpty() || !ifstat.getAllSuccessorEdges().get(0).explicit)) {
+
+ // move else to the if position
+ ifelsestat.getStats().removeWithKey(ifstat.id);
+
+ ifelsestat.iftype = IfStatement.IFTYPE_IF;
+ ifelsestat.setIfstat(ifelsestat.getElsestat());
+ ifelsestat.setElsestat(null);
+
+ if (ifelsestat.getAllSuccessorEdges().isEmpty() && !ifstat.getAllSuccessorEdges().isEmpty()) {
+ StatEdge endedge = ifstat.getAllSuccessorEdges().get(0);
+
+ ifstat.removeSuccessor(endedge);
+ endedge.setSource(ifelsestat);
+ if (endedge.closure != null) {
+ ifelsestat.getParent().addLabeledEdge(endedge);
+ }
+ ifelsestat.addSuccessor(endedge);
+ }
+
+ ifelsestat.getFirst().removeSuccessor(ifelsestat.getIfEdge());
+
+ ifelsestat.setIfEdge(ifelsestat.getElseEdge());
+ ifelsestat.setElseEdge(null);
+
+ // negate head expression
+ ifelsestat.setNegated(!ifelsestat.isNegated());
+ ifelsestat.getHeadexprentList().set(0, ((IfExprent)ifelsestat.getHeadexprent().copy()).negateIf());
+
+ return true;
+ }
+ }
+ }
+
+
+ boolean replaced = true;
+ while (replaced) {
+ replaced = false;
+
+ List<Object> lstObjects = new ArrayList<Object>(stat.getExprents() == null ? stat.getSequentialObjects() : stat.getExprents());
+
+ for (int i = 0; i < lstObjects.size(); i++) {
+ Object obj = lstObjects.get(i);
+
+ if (obj instanceof Statement) {
+ if (identifySecondaryFunctions((Statement)obj)) {
+ replaced = true;
+ break;
+ }
+ }
+ else if (obj instanceof Exprent) {
+ Exprent retexpr = identifySecondaryFunctions((Exprent)obj, true);
+ if (retexpr != null) {
+ if (stat.getExprents() == null) {
+ // only head expressions can be replaced!
+ stat.replaceExprent((Exprent)obj, retexpr);
+ }
+ else {
+ stat.getExprents().set(i, retexpr);
+ }
+ replaced = true;
+ break;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+
+ private static Exprent identifySecondaryFunctions(Exprent exprent, boolean statement_level) {
+
+ if (exprent.type == Exprent.EXPRENT_FUNCTION) {
+ FunctionExprent fexpr = (FunctionExprent)exprent;
+
+ switch (fexpr.getFunctype()) {
+ case FunctionExprent.FUNCTION_BOOLNOT:
+
+ Exprent retparam = propagateBoolNot(fexpr);
+
+ if (retparam != null) {
+ return retparam;
+ }
+
+ break;
+ case FunctionExprent.FUNCTION_EQ:
+ case FunctionExprent.FUNCTION_NE:
+ case FunctionExprent.FUNCTION_GT:
+ case FunctionExprent.FUNCTION_GE:
+ case FunctionExprent.FUNCTION_LT:
+ case FunctionExprent.FUNCTION_LE:
+ Exprent expr1 = fexpr.getLstOperands().get(0);
+ Exprent expr2 = fexpr.getLstOperands().get(1);
+
+ if (expr1.type == Exprent.EXPRENT_CONST) {
+ expr2 = expr1;
+ expr1 = fexpr.getLstOperands().get(1);
+ }
+
+ if (expr1.type == Exprent.EXPRENT_FUNCTION && expr2.type == Exprent.EXPRENT_CONST) {
+ FunctionExprent funcexpr = (FunctionExprent)expr1;
+ ConstExprent cexpr = (ConstExprent)expr2;
+
+ int functype = funcexpr.getFunctype();
+ if (functype == FunctionExprent.FUNCTION_LCMP || functype == FunctionExprent.FUNCTION_FCMPG ||
+ functype == FunctionExprent.FUNCTION_FCMPL || functype == FunctionExprent.FUNCTION_DCMPG ||
+ functype == FunctionExprent.FUNCTION_DCMPL) {
+
+ int desttype = -1;
+
+ Integer[] destcons = mapNumComparisons.get(fexpr.getFunctype());
+ if (destcons != null) {
+ int index = cexpr.getIntValue() + 1;
+ if (index >= 0 && index <= 2) {
+ Integer destcon = destcons[index];
+ if (destcon != null) {
+ desttype = destcon.intValue();
+ }
+ }
+ }
+
+ if (desttype >= 0) {
+ return new FunctionExprent(desttype, funcexpr.getLstOperands());
+ }
+ }
+ }
+ }
+ }
+
+
+ boolean replaced = true;
+ while (replaced) {
+ replaced = false;
+
+ for (Exprent expr : exprent.getAllExprents()) {
+ Exprent retexpr = identifySecondaryFunctions(expr, false);
+ if (retexpr != null) {
+ exprent.replaceExprent(expr, retexpr);
+ replaced = true;
+ break;
+ }
+ }
+ }
+
+ switch (exprent.type) {
+ case Exprent.EXPRENT_FUNCTION:
+ FunctionExprent fexpr = (FunctionExprent)exprent;
+ List<Exprent> lstOperands = fexpr.getLstOperands();
+
+ switch (fexpr.getFunctype()) {
+ case FunctionExprent.FUNCTION_XOR:
+ for (int i = 0; i < 2; i++) {
+ Exprent operand = lstOperands.get(i);
+ VarType operandtype = operand.getExprType();
+
+ if (operand.type == Exprent.EXPRENT_CONST &&
+ operandtype.type != CodeConstants.TYPE_BOOLEAN) {
+ ConstExprent cexpr = (ConstExprent)operand;
+ long val;
+ if (operandtype.type == CodeConstants.TYPE_LONG) {
+ val = ((Long)cexpr.getValue()).longValue();
+ }
+ else {
+ val = ((Integer)cexpr.getValue()).intValue();
+ }
+
+ if (val == -1) {
+ List<Exprent> lstBitNotOperand = new ArrayList<Exprent>();
+ lstBitNotOperand.add(lstOperands.get(1 - i));
+ return new FunctionExprent(FunctionExprent.FUNCTION_BITNOT, lstBitNotOperand);
+ }
+ }
+ }
+ break;
+ case FunctionExprent.FUNCTION_EQ:
+ case FunctionExprent.FUNCTION_NE:
+ if (lstOperands.get(0).getExprType().type == CodeConstants.TYPE_BOOLEAN &&
+ lstOperands.get(1).getExprType().type == CodeConstants.TYPE_BOOLEAN) {
+ for (int i = 0; i < 2; i++) {
+ if (lstOperands.get(i).type == Exprent.EXPRENT_CONST) {
+ ConstExprent cexpr = (ConstExprent)lstOperands.get(i);
+ int val = ((Integer)cexpr.getValue()).intValue();
+
+ if ((fexpr.getFunctype() == FunctionExprent.FUNCTION_EQ && val == 1) ||
+ (fexpr.getFunctype() == FunctionExprent.FUNCTION_NE && val == 0)) {
+ return lstOperands.get(1 - i);
+ }
+ else {
+ List<Exprent> lstNotOperand = new ArrayList<Exprent>();
+ lstNotOperand.add(lstOperands.get(1 - i));
+ return new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, lstNotOperand);
+ }
+ }
+ }
+ }
+ break;
+ case FunctionExprent.FUNCTION_BOOLNOT:
+ if (lstOperands.get(0).type == Exprent.EXPRENT_CONST) {
+ int val = ((ConstExprent)lstOperands.get(0)).getIntValue();
+ if (val == 0) {
+ return new ConstExprent(VarType.VARTYPE_BOOLEAN, new Integer(1));
+ }
+ else {
+ return new ConstExprent(VarType.VARTYPE_BOOLEAN, new Integer(0));
+ }
+ }
+ break;
+ case FunctionExprent.FUNCTION_IIF:
+ Exprent expr1 = lstOperands.get(1);
+ Exprent expr2 = lstOperands.get(2);
+
+ if (expr1.type == Exprent.EXPRENT_CONST && expr2.type == Exprent.EXPRENT_CONST) {
+ ConstExprent cexpr1 = (ConstExprent)expr1;
+ ConstExprent cexpr2 = (ConstExprent)expr2;
+
+ if (cexpr1.getExprType().type == CodeConstants.TYPE_BOOLEAN &&
+ cexpr2.getExprType().type == CodeConstants.TYPE_BOOLEAN) {
+
+ if (cexpr1.getIntValue() == 0 && cexpr2.getIntValue() != 0) {
+ return new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, Arrays.asList(new Exprent[]{lstOperands.get(0)}));
+ }
+ else if (cexpr1.getIntValue() != 0 && cexpr2.getIntValue() == 0) {
+ return lstOperands.get(0);
+ }
+ }
+ }
+ break;
+ case FunctionExprent.FUNCTION_LCMP:
+ case FunctionExprent.FUNCTION_FCMPL:
+ case FunctionExprent.FUNCTION_FCMPG:
+ case FunctionExprent.FUNCTION_DCMPL:
+ case FunctionExprent.FUNCTION_DCMPG:
+ int var = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER);
+ VarType type = lstOperands.get(0).getExprType();
+ VarProcessor processor = (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR);
+
+ FunctionExprent iff = new FunctionExprent(FunctionExprent.FUNCTION_IIF, Arrays.asList(new Exprent[]{
+ new FunctionExprent(FunctionExprent.FUNCTION_LT, Arrays.asList(new Exprent[]{new VarExprent(var, type, processor),
+ ConstExprent.getZeroConstant(type.type)})),
+ new ConstExprent(VarType.VARTYPE_INT, new Integer(-1)),
+ new ConstExprent(VarType.VARTYPE_INT, new Integer(1))}));
+
+ FunctionExprent head = new FunctionExprent(FunctionExprent.FUNCTION_EQ, Arrays.asList(new Exprent[]{
+ new AssignmentExprent(new VarExprent(var, type, processor), new FunctionExprent(FunctionExprent.FUNCTION_SUB,
+ Arrays.asList(
+ new Exprent[]{lstOperands.get(0),
+ lstOperands.get(1)}))),
+ ConstExprent.getZeroConstant(type.type)}));
+
+ processor.setVarType(new VarVersionPaar(var, 0), type);
+
+ return new FunctionExprent(FunctionExprent.FUNCTION_IIF, Arrays.asList(new Exprent[]{
+ head, new ConstExprent(VarType.VARTYPE_INT, new Integer(0)), iff}));
+ }
+ break;
+ case Exprent.EXPRENT_ASSIGNMENT: // check for conditional assignment
+ AssignmentExprent asexpr = (AssignmentExprent)exprent;
+ Exprent right = asexpr.getRight();
+ Exprent left = asexpr.getLeft();
+
+ if (right.type == Exprent.EXPRENT_FUNCTION) {
+ FunctionExprent func = (FunctionExprent)right;
+
+ VarType midlayer = null;
+ if (func.getFunctype() >= FunctionExprent.FUNCTION_I2L &&
+ func.getFunctype() <= FunctionExprent.FUNCTION_I2S) {
+ right = func.getLstOperands().get(0);
+ midlayer = func.getSimpleCastType();
+ if (right.type == Exprent.EXPRENT_FUNCTION) {
+ func = (FunctionExprent)right;
+ }
+ else {
+ return null;
+ }
+ }
+
+ List<Exprent> lstFuncOperands = func.getLstOperands();
+
+ Exprent cond = null;
+
+ switch (func.getFunctype()) {
+ case FunctionExprent.FUNCTION_ADD:
+ case FunctionExprent.FUNCTION_AND:
+ case FunctionExprent.FUNCTION_OR:
+ case FunctionExprent.FUNCTION_XOR:
+ if (left.equals(lstFuncOperands.get(1))) {
+ cond = lstFuncOperands.get(0);
+ break;
+ }
+ case FunctionExprent.FUNCTION_SUB:
+ case FunctionExprent.FUNCTION_MUL:
+ case FunctionExprent.FUNCTION_DIV:
+ case FunctionExprent.FUNCTION_REM:
+ case FunctionExprent.FUNCTION_SHL:
+ case FunctionExprent.FUNCTION_SHR:
+ case FunctionExprent.FUNCTION_USHR:
+ if (left.equals(lstFuncOperands.get(0))) {
+ cond = lstFuncOperands.get(1);
+ }
+ }
+
+ if (cond != null && (midlayer == null || midlayer.equals(cond.getExprType()))) {
+ asexpr.setRight(cond);
+ asexpr.setCondtype(func.getFunctype());
+ }
+ }
+ break;
+ case Exprent.EXPRENT_INVOCATION:
+ if (!statement_level) { // simplify if exprent is a real expression. The opposite case is pretty absurd, can still happen however (and happened at least once).
+ Exprent retexpr = ConcatenationHelper.contractStringConcat(exprent);
+ if (!exprent.equals(retexpr)) {
+ return retexpr;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public static Exprent propagateBoolNot(Exprent exprent) {
+
+ if (exprent.type == Exprent.EXPRENT_FUNCTION) {
+ FunctionExprent fexpr = (FunctionExprent)exprent;
+
+ if (fexpr.getFunctype() == FunctionExprent.FUNCTION_BOOLNOT) {
+
+ Exprent param = fexpr.getLstOperands().get(0);
+
+ if (param.type == Exprent.EXPRENT_FUNCTION) {
+ FunctionExprent fparam = (FunctionExprent)param;
+
+ int ftype = fparam.getFunctype();
+ switch (ftype) {
+ case FunctionExprent.FUNCTION_BOOLNOT:
+ Exprent newexpr = fparam.getLstOperands().get(0);
+ Exprent retexpr = propagateBoolNot(newexpr);
+ return retexpr == null ? newexpr : retexpr;
+ case FunctionExprent.FUNCTION_CADD:
+ case FunctionExprent.FUNCTION_COR:
+ List<Exprent> operands = fparam.getLstOperands();
+ for (int i = 0; i < operands.size(); i++) {
+ Exprent newparam = new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT,
+ Arrays.asList(new Exprent[]{operands.get(i)}));
+
+ Exprent retparam = propagateBoolNot(newparam);
+ operands.set(i, retparam == null ? newparam : retparam);
+ }
+ case FunctionExprent.FUNCTION_EQ:
+ case FunctionExprent.FUNCTION_NE:
+ case FunctionExprent.FUNCTION_LT:
+ case FunctionExprent.FUNCTION_GE:
+ case FunctionExprent.FUNCTION_GT:
+ case FunctionExprent.FUNCTION_LE:
+ fparam.setFunctype(funcsnot[ftype - FunctionExprent.FUNCTION_EQ]);
+ return fparam;
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java
new file mode 100644
index 000000000000..063d201f81f0
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java
@@ -0,0 +1,327 @@
+/*
+ * 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.modules.decompiler;
+
+import org.jetbrains.java.decompiler.code.cfg.BasicBlock;
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+
+public class SequenceHelper {
+
+
+ public static void condenseSequences(Statement root) {
+ condenseSequencesRec(root);
+ }
+
+ private static void condenseSequencesRec(Statement stat) {
+
+ if (stat.type == Statement.TYPE_SEQUENCE) {
+
+ List<Statement> lst = new ArrayList<Statement>();
+ lst.addAll(stat.getStats());
+
+ boolean unfolded = false;
+
+ // unfold blocks
+ for (int i = 0; i < lst.size(); i++) {
+ Statement st = lst.get(i);
+ if (st.type == Statement.TYPE_SEQUENCE) {
+
+ removeEmptyStatements((SequenceStatement)st);
+
+ if (i == lst.size() - 1 || isSequenceDisbandable(st, lst.get(i + 1))) {
+ // move predecessors
+ Statement first = st.getFirst();
+ for (StatEdge edge : st.getAllPredecessorEdges()) {
+ st.removePredecessor(edge);
+ edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, first);
+ first.addPredecessor(edge);
+ }
+
+ // move successors
+ Statement last = st.getStats().getLast();
+ if (last.getAllSuccessorEdges().isEmpty() && i < lst.size() - 1) {
+ last.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, last, lst.get(i + 1)));
+ }
+ else {
+ for (StatEdge edge : last.getAllSuccessorEdges()) {
+ if (i == lst.size() - 1) {
+ if (edge.closure == st) {
+ stat.addLabeledEdge(edge);
+ }
+ }
+ else {
+ edge.getSource().changeEdgeType(Statement.DIRECTION_FORWARD, edge, StatEdge.TYPE_REGULAR);
+ edge.closure.getLabelEdges().remove(edge);
+ edge.closure = null;
+ }
+ }
+ }
+
+ for (StatEdge edge : st.getAllSuccessorEdges()) {
+ st.removeSuccessor(edge);
+ }
+
+ for (StatEdge edge : new HashSet<StatEdge>(st.getLabelEdges())) {
+ if (edge.getSource() != last) {
+ last.addLabeledEdge(edge);
+ }
+ }
+
+ lst.remove(i);
+ lst.addAll(i, st.getStats());
+ i--;
+
+ unfolded = true;
+ }
+ }
+ }
+
+ if (unfolded) {
+ SequenceStatement sequence = new SequenceStatement(lst);
+ sequence.setAllParent();
+
+ stat.getParent().replaceStatement(stat, sequence);
+
+ stat = sequence;
+ }
+ }
+
+ // sequence consisting of one statement -> disband
+ if (stat.type == Statement.TYPE_SEQUENCE) {
+
+ removeEmptyStatements((SequenceStatement)stat);
+
+ if (stat.getStats().size() == 1) {
+
+ Statement st = stat.getFirst();
+
+ boolean ok = st.getAllSuccessorEdges().isEmpty();
+ if (!ok) {
+ StatEdge edge = st.getAllSuccessorEdges().get(0);
+
+ ok = stat.getAllSuccessorEdges().isEmpty();
+ if (!ok) {
+ StatEdge statedge = stat.getAllSuccessorEdges().get(0);
+ ok = (edge.getDestination() == statedge.getDestination());
+
+ if (ok) {
+ st.removeSuccessor(edge);
+ }
+ }
+ }
+
+ if (ok) {
+ stat.getParent().replaceStatement(stat, st);
+ stat = st;
+ }
+ }
+ }
+
+ // replace flat statements with synthetic basic blocks
+ outer:
+ while (true) {
+ for (Statement st : stat.getStats()) {
+ if ((st.getStats().isEmpty() || st.getExprents() != null) && st.type != Statement.TYPE_BASICBLOCK) {
+ destroyAndFlattenStatement(st);
+ continue outer;
+ }
+ }
+ break;
+ }
+
+ // recursion
+ for (int i = 0; i < stat.getStats().size(); i++) {
+ condenseSequencesRec(stat.getStats().get(i));
+ }
+ }
+
+ private static boolean isSequenceDisbandable(Statement block, Statement next) {
+
+ Statement last = block.getStats().getLast();
+ List<StatEdge> lstSuccs = last.getAllSuccessorEdges();
+ if (!lstSuccs.isEmpty()) {
+ if (lstSuccs.get(0).getDestination() != next) {
+ return false;
+ }
+ }
+
+ for (StatEdge edge : next.getPredecessorEdges(StatEdge.TYPE_BREAK)) {
+ if (last != edge.getSource() && !last.containsStatementStrict(edge.getSource())) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static void removeEmptyStatements(SequenceStatement sequence) {
+
+ if (sequence.getStats().size() <= 1) {
+ return;
+ }
+
+ mergeFlatStatements(sequence);
+
+ while (true) {
+
+ boolean found = false;
+
+ for (Statement st : sequence.getStats()) {
+
+ if (st.getExprents() != null && st.getExprents().isEmpty()) {
+
+ if (st.getAllSuccessorEdges().isEmpty()) {
+ List<StatEdge> lstBreaks = st.getPredecessorEdges(StatEdge.TYPE_BREAK);
+
+ if (lstBreaks.isEmpty()) {
+ for (StatEdge edge : st.getAllPredecessorEdges()) {
+ edge.getSource().removeSuccessor(edge);
+ }
+ found = true;
+ }
+ }
+ else {
+ StatEdge sucedge = st.getAllSuccessorEdges().get(0);
+ if (sucedge.getType() != StatEdge.TYPE_FINALLYEXIT) {
+ st.removeSuccessor(sucedge);
+
+ for (StatEdge edge : st.getAllPredecessorEdges()) {
+ if (sucedge.getType() != StatEdge.TYPE_REGULAR) {
+ edge.getSource().changeEdgeType(Statement.DIRECTION_FORWARD, edge, sucedge.getType());
+ }
+
+ st.removePredecessor(edge);
+ edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, sucedge.getDestination());
+ sucedge.getDestination().addPredecessor(edge);
+
+ if (sucedge.closure != null) {
+ sucedge.closure.addLabeledEdge(edge);
+ }
+ }
+ found = true;
+ }
+ }
+
+ if (found) {
+ sequence.getStats().removeWithKey(st.id);
+ break;
+ }
+ }
+ }
+
+ if (!found) {
+ break;
+ }
+ }
+
+ sequence.setFirst(sequence.getStats().get(0));
+ }
+
+ private static void mergeFlatStatements(SequenceStatement sequence) {
+
+ while (true) {
+
+ Statement next = null;
+ Statement current = null;
+
+ boolean found = false;
+
+ for (int i = sequence.getStats().size() - 1; i >= 0; i--) {
+
+ next = current;
+ current = sequence.getStats().get(i);
+
+ if (next != null && current.getExprents() != null && !current.getExprents().isEmpty()) {
+ if (next.getExprents() != null) {
+ next.getExprents().addAll(0, current.getExprents());
+ current.getExprents().clear();
+ found = true;
+ }
+ else {
+ Statement first = getFirstExprentlist(next);
+ if (first != null) {
+ first.getExprents().addAll(0, current.getExprents());
+ current.getExprents().clear();
+ found = true;
+ }
+ }
+ }
+ }
+
+ if (!found) {
+ break;
+ }
+ }
+ }
+
+ private static Statement getFirstExprentlist(Statement stat) {
+
+ if (stat.getExprents() != null) {
+ return stat;
+ }
+
+ switch (stat.type) {
+ case Statement.TYPE_IF:
+ case Statement.TYPE_SEQUENCE:
+ case Statement.TYPE_SWITCH:
+ case Statement.TYPE_SYNCRONIZED:
+ return getFirstExprentlist(stat.getFirst());
+ }
+
+ return null;
+ }
+
+
+ public static void destroyAndFlattenStatement(Statement stat) {
+
+ destroyStatementContent(stat, false);
+
+ BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock(
+ DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)));
+ if (stat.getExprents() == null) {
+ bstat.setExprents(new ArrayList<Exprent>());
+ }
+ else {
+ bstat.setExprents(DecHelper.copyExprentList(stat.getExprents()));
+ }
+
+ stat.getParent().replaceStatement(stat, bstat);
+ }
+
+ public static void destroyStatementContent(Statement stat, boolean self) {
+
+ for (Statement st : stat.getStats()) {
+ destroyStatementContent(st, true);
+ }
+ stat.getStats().clear();
+
+ if (self) {
+ for (StatEdge edge : stat.getAllSuccessorEdges()) {
+ stat.removeSuccessor(edge);
+ }
+ }
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java
new file mode 100644
index 000000000000..90ebe2f8218d
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java
@@ -0,0 +1,853 @@
+/*
+ * 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.modules.decompiler;
+
+import org.jetbrains.java.decompiler.code.CodeConstants;
+import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
+import org.jetbrains.java.decompiler.modules.decompiler.sforms.SSAConstructorSparseEx;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar;
+import org.jetbrains.java.decompiler.struct.StructClass;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+public class SimplifyExprentsHelper {
+
+ private boolean firstInvocation;
+
+ public SimplifyExprentsHelper(boolean firstInvocation) {
+ this.firstInvocation = firstInvocation;
+ }
+
+ public boolean simplifyStackVarsStatement(Statement stat, HashSet<Integer> setReorderedIfs, SSAConstructorSparseEx ssa, StructClass cl) {
+
+ boolean res = false;
+
+ if (stat.getExprents() == null) {
+
+ while (true) {
+
+ boolean changed = false;
+
+ for (Statement st : stat.getStats()) {
+ res |= simplifyStackVarsStatement(st, setReorderedIfs, ssa, cl);
+
+ // collapse composed if's
+ if (changed = IfHelper.mergeIfs(st, setReorderedIfs)) {
+ break;
+ }
+
+ // collapse iff ?: statement
+ if (changed = buildIff(st, ssa)) {
+ break;
+ }
+ }
+
+ res |= changed;
+
+ if (!changed) {
+ break;
+ }
+ }
+ }
+ else {
+ res |= simplifyStackVarsExprents(stat.getExprents(), cl);
+ }
+
+ return res;
+ }
+
+ private boolean simplifyStackVarsExprents(List<Exprent> list, StructClass cl) {
+
+ boolean res = false;
+
+ int index = 0;
+
+ while (index < list.size()) {
+
+ Exprent current = list.get(index);
+
+ Exprent ret = isSimpleConstructorInvocation(current);
+ if (ret != null) {
+ list.set(index, ret);
+ res = true;
+
+ continue;
+ }
+
+ // lambda expression (Java 8)
+ ret = isLambda(current, cl);
+ if (ret != null) {
+ list.set(index, ret);
+ res = true;
+
+ continue;
+ }
+
+ // remove monitor exit
+ if (isMonitorExit(current)) {
+ list.remove(index);
+ res = true;
+
+ continue;
+ }
+
+ // trivial assignment of a stack variable
+ if (isTrivialStackAssignment(current)) {
+ list.remove(index);
+ res = true;
+
+ continue;
+ }
+
+ if (index == list.size() - 1) {
+ break;
+ }
+
+
+ Exprent next = list.get(index + 1);
+
+
+ // constructor invocation
+ if (isConstructorInvocationRemote(list, index)) {
+ list.remove(index);
+ res = true;
+
+ continue;
+ }
+
+ // remove getClass() invocation, which is part of a qualified new
+ if (DecompilerContext.getOption(IFernflowerPreferences.REMOVE_GET_CLASS_NEW)) {
+ if (isQualifiedNewGetClass(current, next)) {
+ list.remove(index);
+ res = true;
+
+ continue;
+ }
+ }
+
+ // direct initialization of an array
+ int arrcount = isArrayInitializer(list, index);
+ if (arrcount > 0) {
+ for (int i = 0; i < arrcount; i++) {
+ list.remove(index + 1);
+ }
+ res = true;
+
+ continue;
+ }
+
+ // add array initializer expression
+ if (addArrayInitializer(current, next)) {
+ list.remove(index + 1);
+ res = true;
+
+ continue;
+ }
+
+ // integer ++expr and --expr (except for vars!)
+ Exprent func = isPPIorMMI(current);
+ if (func != null) {
+ list.set(index, func);
+ res = true;
+
+ continue;
+ }
+
+ // expr++ and expr--
+ if (isIPPorIMM(current, next)) {
+ list.remove(index + 1);
+ res = true;
+
+ continue;
+ }
+
+ // assignment on stack
+ if (isStackAssignement(current, next)) {
+ list.remove(index + 1);
+ res = true;
+
+ continue;
+ }
+
+ if (!firstInvocation && isStackAssignement2(current, next)) {
+ list.remove(index + 1);
+ res = true;
+
+ continue;
+ }
+
+ index++;
+ }
+
+ return res;
+ }
+
+ private static boolean addArrayInitializer(Exprent first, Exprent second) {
+
+ if (first.type == Exprent.EXPRENT_ASSIGNMENT) {
+ AssignmentExprent as = (AssignmentExprent)first;
+
+ if (as.getRight().type == Exprent.EXPRENT_NEW && as.getLeft().type == Exprent.EXPRENT_VAR) {
+ NewExprent newex = (NewExprent)as.getRight();
+
+ if (!newex.getLstArrayElements().isEmpty()) {
+
+ VarExprent arrvar = (VarExprent)as.getLeft();
+
+ if (second.type == Exprent.EXPRENT_ASSIGNMENT) {
+ AssignmentExprent aas = (AssignmentExprent)second;
+ if (aas.getLeft().type == Exprent.EXPRENT_ARRAY) {
+ ArrayExprent arrex = (ArrayExprent)aas.getLeft();
+ if (arrex.getArray().type == Exprent.EXPRENT_VAR && arrvar.equals(arrex.getArray())
+ && arrex.getIndex().type == Exprent.EXPRENT_CONST) {
+
+ int constvalue = ((ConstExprent)arrex.getIndex()).getIntValue();
+
+ if (constvalue < newex.getLstArrayElements().size()) {
+ Exprent init = newex.getLstArrayElements().get(constvalue);
+ if (init.type == Exprent.EXPRENT_CONST) {
+ ConstExprent cinit = (ConstExprent)init;
+
+ VarType arrtype = newex.getNewtype().copy();
+ arrtype.decArrayDim();
+
+ ConstExprent defaultval = ExprProcessor.getDefaultArrayValue(arrtype);
+
+ if (cinit.equals(defaultval)) {
+
+ Exprent tempexpr = aas.getRight();
+
+ if (!tempexpr.containsExprent(arrvar)) {
+ newex.getLstArrayElements().set(constvalue, tempexpr);
+
+ if (tempexpr.type == Exprent.EXPRENT_NEW) {
+ NewExprent tempnewex = (NewExprent)tempexpr;
+ int dims = newex.getNewtype().arraydim;
+ if (dims > 1 && !tempnewex.getLstArrayElements().isEmpty()) {
+ tempnewex.setDirectArrayInit(true);
+ }
+ }
+
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+
+ private static int isArrayInitializer(List<Exprent> list, int index) {
+
+ Exprent current = list.get(index);
+ if (current.type == Exprent.EXPRENT_ASSIGNMENT) {
+ AssignmentExprent as = (AssignmentExprent)current;
+
+ if (as.getRight().type == Exprent.EXPRENT_NEW && as.getLeft().type == Exprent.EXPRENT_VAR) {
+ NewExprent newex = (NewExprent)as.getRight();
+
+ if (newex.getExprType().arraydim > 0 && newex.getLstDims().size() == 1 && newex.getLstArrayElements().isEmpty() &&
+ newex.getLstDims().get(0).type == Exprent.EXPRENT_CONST) {
+
+ int size = ((Integer)((ConstExprent)newex.getLstDims().get(0)).getValue()).intValue();
+ if (size == 0) {
+ return 0;
+ }
+
+ VarExprent arrvar = (VarExprent)as.getLeft();
+
+ HashMap<Integer, Exprent> mapInit = new HashMap<Integer, Exprent>();
+
+ int i = 1;
+ while (index + i < list.size() && i <= size) {
+ boolean found = false;
+
+ Exprent expr = list.get(index + i);
+ if (expr.type == Exprent.EXPRENT_ASSIGNMENT) {
+ AssignmentExprent aas = (AssignmentExprent)expr;
+ if (aas.getLeft().type == Exprent.EXPRENT_ARRAY) {
+ ArrayExprent arrex = (ArrayExprent)aas.getLeft();
+ if (arrex.getArray().type == Exprent.EXPRENT_VAR && arrvar.equals(arrex.getArray())
+ && arrex.getIndex().type == Exprent.EXPRENT_CONST) {
+
+ int constvalue = ((ConstExprent)arrex.getIndex())
+ .getIntValue(); // TODO: check for a number type. Failure extremely improbable, but nevertheless...
+
+ if (constvalue < size && !mapInit.containsKey(constvalue)) {
+
+ if (!aas.getRight().containsExprent(arrvar)) {
+ mapInit.put(constvalue, aas.getRight());
+ found = true;
+ }
+ }
+ }
+ }
+ }
+
+ if (!found) {
+ break;
+ }
+
+ i++;
+ }
+
+ double fraction = ((double)mapInit.size()) / size;
+
+ if ((arrvar.isStack() && fraction > 0) || (size <= 7 && fraction >= 0.3) ||
+ (size > 7 && fraction >= 0.7)) {
+
+ List<Exprent> lstRet = new ArrayList<Exprent>();
+
+ VarType arrtype = newex.getNewtype().copy();
+ arrtype.decArrayDim();
+
+ ConstExprent defaultval = ExprProcessor.getDefaultArrayValue(arrtype);
+
+ for (int j = 0; j < size; j++) {
+ lstRet.add(defaultval.copy());
+ }
+
+ int dims = newex.getNewtype().arraydim;
+ for (Entry<Integer, Exprent> ent : mapInit.entrySet()) {
+ Exprent tempexpr = ent.getValue();
+ lstRet.set(ent.getKey(), tempexpr);
+
+ if (tempexpr.type == Exprent.EXPRENT_NEW) {
+ NewExprent tempnewex = (NewExprent)tempexpr;
+ if (dims > 1 && !tempnewex.getLstArrayElements().isEmpty()) {
+ tempnewex.setDirectArrayInit(true);
+ }
+ }
+ }
+
+ newex.setLstArrayElements(lstRet);
+
+ return mapInit.size();
+ }
+ }
+ }
+ }
+
+ return 0;
+ }
+
+ private static boolean isTrivialStackAssignment(Exprent first) {
+
+ if (first.type == Exprent.EXPRENT_ASSIGNMENT) {
+ AssignmentExprent asf = (AssignmentExprent)first;
+
+ if (asf.getLeft().type == Exprent.EXPRENT_VAR && asf.getRight().type == Exprent.EXPRENT_VAR) {
+ VarExprent varleft = (VarExprent)asf.getLeft();
+ VarExprent varright = (VarExprent)asf.getRight();
+
+ if (varleft.getIndex() == varright.getIndex() && varleft.isStack() &&
+ varright.isStack()) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private static boolean isStackAssignement2(Exprent first, Exprent second) { // e.g. 1.4-style class invocation
+
+ if (first.type == Exprent.EXPRENT_ASSIGNMENT && second.type == Exprent.EXPRENT_ASSIGNMENT) {
+ AssignmentExprent asf = (AssignmentExprent)first;
+ AssignmentExprent ass = (AssignmentExprent)second;
+
+ if (asf.getLeft().type == Exprent.EXPRENT_VAR && ass.getRight().type == Exprent.EXPRENT_VAR &&
+ asf.getLeft().equals(ass.getRight()) && ((VarExprent)asf.getLeft()).isStack()) {
+ if (ass.getLeft().type != Exprent.EXPRENT_VAR || !((VarExprent)ass.getLeft()).isStack()) {
+ asf.setRight(new AssignmentExprent(ass.getLeft(), asf.getRight()));
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private static boolean isStackAssignement(Exprent first, Exprent second) {
+
+ if (first.type == Exprent.EXPRENT_ASSIGNMENT && second.type == Exprent.EXPRENT_ASSIGNMENT) {
+ AssignmentExprent asf = (AssignmentExprent)first;
+ AssignmentExprent ass = (AssignmentExprent)second;
+
+ while (true) {
+ if (asf.getRight().equals(ass.getRight())) {
+ if ((asf.getLeft().type == Exprent.EXPRENT_VAR && ((VarExprent)asf.getLeft()).isStack()) &&
+ (ass.getLeft().type != Exprent.EXPRENT_VAR || !((VarExprent)ass.getLeft()).isStack())) {
+
+ if (!ass.getLeft().containsExprent(asf.getLeft())) {
+ asf.setRight(ass);
+ return true;
+ }
+ }
+ }
+ if (asf.getRight().type == Exprent.EXPRENT_ASSIGNMENT) {
+ asf = (AssignmentExprent)asf.getRight();
+ }
+ else {
+ break;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private static Exprent isPPIorMMI(Exprent first) {
+
+ if (first.type == Exprent.EXPRENT_ASSIGNMENT) {
+ AssignmentExprent as = (AssignmentExprent)first;
+
+ if (as.getRight().type == Exprent.EXPRENT_FUNCTION) {
+ FunctionExprent func = (FunctionExprent)as.getRight();
+
+ if (func.getFunctype() == FunctionExprent.FUNCTION_ADD ||
+ func.getFunctype() == FunctionExprent.FUNCTION_SUB) {
+ Exprent econd = func.getLstOperands().get(0);
+ Exprent econst = func.getLstOperands().get(1);
+
+ if (econst.type != Exprent.EXPRENT_CONST && econd.type == Exprent.EXPRENT_CONST &&
+ func.getFunctype() == FunctionExprent.FUNCTION_ADD) {
+ econd = econst;
+ econst = func.getLstOperands().get(0);
+ }
+
+ if (econst.type == Exprent.EXPRENT_CONST && ((ConstExprent)econst).hasValueOne()) {
+ Exprent left = as.getLeft();
+
+ if (left.type != Exprent.EXPRENT_VAR && left.equals(econd)) {
+ FunctionExprent ret = new FunctionExprent(
+ func.getFunctype() == FunctionExprent.FUNCTION_ADD ? FunctionExprent.FUNCTION_PPI : FunctionExprent.FUNCTION_MMI,
+ Arrays.asList(new Exprent[]{econd}));
+ ret.setImplicitType(VarType.VARTYPE_INT);
+ return ret;
+ }
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private static boolean isIPPorIMM(Exprent first, Exprent second) {
+
+ if (first.type == Exprent.EXPRENT_ASSIGNMENT && second.type == Exprent.EXPRENT_FUNCTION) {
+ AssignmentExprent as = (AssignmentExprent)first;
+ FunctionExprent in = (FunctionExprent)second;
+
+ if ((in.getFunctype() == FunctionExprent.FUNCTION_MMI || in.getFunctype() == FunctionExprent.FUNCTION_PPI) &&
+ in.getLstOperands().get(0).equals(as.getRight())) {
+
+ if (in.getFunctype() == FunctionExprent.FUNCTION_MMI) {
+ in.setFunctype(FunctionExprent.FUNCTION_IMM);
+ }
+ else {
+ in.setFunctype(FunctionExprent.FUNCTION_IPP);
+ }
+ as.setRight(in);
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static boolean isMonitorExit(Exprent first) {
+ if (first.type == Exprent.EXPRENT_MONITOR) {
+ MonitorExprent monexpr = (MonitorExprent)first;
+ if (monexpr.getMontype() == MonitorExprent.MONITOR_EXIT && monexpr.getValue().type == Exprent.EXPRENT_VAR
+ && !((VarExprent)monexpr.getValue()).isStack()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static boolean isQualifiedNewGetClass(Exprent first, Exprent second) {
+
+ if (first.type == Exprent.EXPRENT_INVOCATION) {
+ InvocationExprent invexpr = (InvocationExprent)first;
+
+ if (!invexpr.isStatic() && invexpr.getInstance().type == Exprent.EXPRENT_VAR && invexpr.getName().equals("getClass") &&
+ invexpr.getStringDescriptor().equals("()Ljava/lang/Class;")) {
+
+ List<Exprent> lstExprents = second.getAllExprents();
+ lstExprents.add(second);
+
+ for (Exprent expr : lstExprents) {
+ if (expr.type == Exprent.EXPRENT_NEW) {
+ NewExprent nexpr = (NewExprent)expr;
+ if (nexpr.getConstructor() != null && !nexpr.getConstructor().getLstParameters().isEmpty() &&
+ nexpr.getConstructor().getLstParameters().get(0).equals(invexpr.getInstance())) {
+
+ String classname = nexpr.getNewtype().value;
+ ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(classname);
+ if (node != null && node.type != ClassNode.CLASS_ROOT) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // private static boolean isConstructorInvocationRemote(List<Exprent> list, int index) {
+ //
+ // Exprent current = list.get(index);
+ //
+ // if(current.type == Exprent.EXPRENT_ASSIGNMENT) {
+ // AssignmentExprent as = (AssignmentExprent)current;
+ //
+ // if(as.getLeft().type == Exprent.EXPRENT_VAR && as.getRight().type == Exprent.EXPRENT_NEW) {
+ //
+ // NewExprent newexpr = (NewExprent)as.getRight();
+ // VarType newtype = newexpr.getNewtype();
+ // VarVersionPaar leftPaar = new VarVersionPaar((VarExprent)as.getLeft());
+ //
+ // if(newtype.type == CodeConstants.TYPE_OBJECT && newtype.arraydim == 0 &&
+ // newexpr.getConstructor() == null) {
+ //
+ // Set<VarVersionPaar> setChangedVars = new HashSet<VarVersionPaar>();
+ //
+ // for(int i = index + 1; i < list.size(); i++) {
+ // Exprent remote = list.get(i);
+ //
+ // if(remote.type == Exprent.EXPRENT_INVOCATION) {
+ // InvocationExprent in = (InvocationExprent)remote;
+ //
+ // if(in.getFunctype() == InvocationExprent.TYP_INIT && in.getInstance().type == Exprent.EXPRENT_VAR
+ // && as.getLeft().equals(in.getInstance())) {
+ //
+ // Set<VarVersionPaar> setVars = remote.getAllVariables();
+ // setVars.remove(leftPaar);
+ // setVars.retainAll(setChangedVars);
+ //
+ // if(setVars.isEmpty()) {
+ //
+ // newexpr.setConstructor(in);
+ // in.setInstance(null);
+ //
+ // if(!setChangedVars.isEmpty()) { // some exprents inbetween
+ // list.add(index+1, as.copy());
+ // list.remove(i+1);
+ // } else {
+ // list.set(i, as.copy());
+ // }
+ //
+ // return true;
+ // }
+ // }
+ // }
+ //
+ // boolean isTempAssignment = false;
+ //
+ // if(remote.type == Exprent.EXPRENT_ASSIGNMENT) { // ugly solution
+ // AssignmentExprent asremote = (AssignmentExprent)remote;
+ // if(asremote.getLeft().type == Exprent.EXPRENT_VAR &&
+ // asremote.getRight().type == Exprent.EXPRENT_VAR) {
+ // setChangedVars.add(new VarVersionPaar((VarExprent)asremote.getLeft()));
+ // isTempAssignment = true;
+ // }
+ //
+ // // FIXME: needs to be rewritten
+ // // propagate (var = new X) forward to the <init> invokation and then reduce
+ //
+ //// if(asremote.getLeft().type == Exprent.EXPRENT_VAR) {
+ //// List<Exprent> lstRightExprents = asremote.getRight().getAllExprents(true);
+ //// lstRightExprents.add(asremote.getRight());
+ ////
+ //// Set<VarVersionPaar> setTempChangedVars = new HashSet<VarVersionPaar>();
+ //// boolean isTemp = true;
+ ////
+ //// for(Exprent expr : lstRightExprents) {
+ //// if(expr.type != Exprent.EXPRENT_VAR && expr.type != Exprent.EXPRENT_FIELD) {
+ //// isTemp = false;
+ //// break;
+ //// } else if(expr.type == Exprent.EXPRENT_VAR) {
+ //// setTempChangedVars.add(new VarVersionPaar((VarExprent)expr));
+ //// }
+ //// }
+ ////
+ //// if(isTemp) {
+ //// setChangedVars.addAll(setTempChangedVars);
+ //// isTempAssignment = true;
+ //// }
+ //// }
+ //// } else if(remote.type == Exprent.EXPRENT_FUNCTION) {
+ //// FunctionExprent fexpr = (FunctionExprent)remote;
+ //// if(fexpr.getFunctype() == FunctionExprent.FUNCTION_IPP || fexpr.getFunctype() == FunctionExprent.FUNCTION_IMM
+ //// || fexpr.getFunctype() == FunctionExprent.FUNCTION_PPI || fexpr.getFunctype() == FunctionExprent.FUNCTION_MMI) {
+ //// if(fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_VAR) {
+ //// setChangedVars.add(new VarVersionPaar((VarExprent)fexpr.getLstOperands().get(0)));
+ //// isTempAssignment = true;
+ //// }
+ //// }
+ // }
+ //
+ // if(!isTempAssignment) {
+ // Set<VarVersionPaar> setVars = remote.getAllVariables();
+ // if(setVars.contains(leftPaar)) {
+ // return false;
+ // } else {
+ // setChangedVars.addAll(setVars);
+ // }
+ // }
+ // }
+ // }
+ // }
+ // }
+ //
+ // return false;
+ // }
+
+ // propagate (var = new X) forward to the <init> invokation
+ private static boolean isConstructorInvocationRemote(List<Exprent> list, int index) {
+
+ Exprent current = list.get(index);
+
+ if (current.type == Exprent.EXPRENT_ASSIGNMENT) {
+ AssignmentExprent as = (AssignmentExprent)current;
+
+ if (as.getLeft().type == Exprent.EXPRENT_VAR && as.getRight().type == Exprent.EXPRENT_NEW) {
+
+ NewExprent newexpr = (NewExprent)as.getRight();
+ VarType newtype = newexpr.getNewtype();
+ VarVersionPaar leftPaar = new VarVersionPaar((VarExprent)as.getLeft());
+
+ if (newtype.type == CodeConstants.TYPE_OBJECT && newtype.arraydim == 0 && newexpr.getConstructor() == null) {
+
+ for (int i = index + 1; i < list.size(); i++) {
+ Exprent remote = list.get(i);
+
+ // <init> invocation
+ if (remote.type == Exprent.EXPRENT_INVOCATION) {
+ InvocationExprent in = (InvocationExprent)remote;
+
+ if (in.getFunctype() == InvocationExprent.TYP_INIT &&
+ in.getInstance().type == Exprent.EXPRENT_VAR &&
+ as.getLeft().equals(in.getInstance())) {
+
+ newexpr.setConstructor(in);
+ in.setInstance(null);
+
+ list.set(i, as.copy());
+
+ return true;
+ }
+ }
+
+ // check for variable in use
+ Set<VarVersionPaar> setVars = remote.getAllVariables();
+ if (setVars.contains(leftPaar)) { // variable used somewhere in between -> exit, need a better reduced code
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private static Exprent isLambda(Exprent exprent, StructClass cl) {
+
+ List<Exprent> lst = exprent.getAllExprents();
+ for (Exprent expr : lst) {
+ Exprent ret = isLambda(expr, cl);
+ if (ret != null) {
+ exprent.replaceExprent(expr, ret);
+ }
+ }
+
+ if (exprent.type == Exprent.EXPRENT_INVOCATION) {
+ InvocationExprent in = (InvocationExprent)exprent;
+
+ if (in.getInvocationTyp() == InvocationExprent.INVOKE_DYNAMIC) {
+
+ String lambda_class_name = cl.qualifiedName + in.getInvokeDynamicClassSuffix();
+ ClassNode lambda_class = DecompilerContext.getClassProcessor().getMapRootClasses().get(lambda_class_name);
+
+ if (lambda_class != null) { // real lambda class found, replace invocation with an anonymous class
+
+ NewExprent newexp = new NewExprent(new VarType(lambda_class_name, true), null, 0);
+ newexp.setConstructor(in);
+ // note: we don't set the instance to null with in.setInstance(null) like it is done for a common constructor invokation
+ // lambda can also be a reference to a virtual method (e.g. String x; ...(x::toString);)
+ // in this case instance will hold the corresponding object
+
+ return newexp;
+ }
+ }
+ }
+
+ return null;
+ }
+
+
+ private static Exprent isSimpleConstructorInvocation(Exprent exprent) {
+
+ List<Exprent> lst = exprent.getAllExprents();
+ for (Exprent expr : lst) {
+ Exprent ret = isSimpleConstructorInvocation(expr);
+ if (ret != null) {
+ exprent.replaceExprent(expr, ret);
+ }
+ }
+
+ if (exprent.type == Exprent.EXPRENT_INVOCATION) {
+ InvocationExprent in = (InvocationExprent)exprent;
+ if (in.getFunctype() == InvocationExprent.TYP_INIT && in.getInstance().type == Exprent.EXPRENT_NEW) {
+ NewExprent newexp = (NewExprent)in.getInstance();
+ newexp.setConstructor(in);
+ in.setInstance(null);
+ return newexp;
+ }
+ }
+
+ return null;
+ }
+
+
+ private static boolean buildIff(Statement stat, SSAConstructorSparseEx ssa) {
+
+ if (stat.type == Statement.TYPE_IF && stat.getExprents() == null) {
+ IfStatement stif = (IfStatement)stat;
+ if (stif.iftype == IfStatement.IFTYPE_IFELSE) {
+ Statement ifstat = stif.getIfstat();
+ Statement elsestat = stif.getElsestat();
+
+ if (ifstat.getExprents() != null && ifstat.getExprents().size() == 1
+ && elsestat.getExprents() != null && elsestat.getExprents().size() == 1
+ && ifstat.getAllSuccessorEdges().size() == 1 && elsestat.getAllSuccessorEdges().size() == 1
+ && ifstat.getAllSuccessorEdges().get(0).getDestination() == elsestat.getAllSuccessorEdges().get(0).getDestination()) {
+
+ Exprent ifexpr = ifstat.getExprents().get(0);
+ Exprent elseexpr = elsestat.getExprents().get(0);
+
+ if (ifexpr.type == Exprent.EXPRENT_ASSIGNMENT && elseexpr.type == Exprent.EXPRENT_ASSIGNMENT) {
+ AssignmentExprent ifas = (AssignmentExprent)ifexpr;
+ AssignmentExprent elseas = (AssignmentExprent)elseexpr;
+
+ if (ifas.getLeft().type == Exprent.EXPRENT_VAR && elseas.getLeft().type == Exprent.EXPRENT_VAR) {
+ VarExprent ifvar = (VarExprent)ifas.getLeft();
+ VarExprent elsevar = (VarExprent)elseas.getLeft();
+
+ if (ifvar.getIndex() == elsevar.getIndex() && ifvar.isStack()) { // ifvar.getIndex() >= VarExprent.STACK_BASE) {
+
+ boolean found = false;
+
+ for (Entry<VarVersionPaar, FastSparseSet<Integer>> ent : ssa.getPhi().entrySet()) {
+ if (ent.getKey().var == ifvar.getIndex()) {
+ if (ent.getValue().contains(ifvar.getVersion()) && ent.getValue().contains(elsevar.getVersion())) {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (found) {
+ List<Exprent> data = new ArrayList<Exprent>();
+ data.addAll(stif.getFirst().getExprents());
+
+ data.add(new AssignmentExprent(ifvar, new FunctionExprent(FunctionExprent.FUNCTION_IIF,
+ Arrays.asList(new Exprent[]{
+ stif.getHeadexprent().getCondition(),
+ ifas.getRight(),
+ elseas.getRight()}))));
+ stif.setExprents(data);
+
+ if (stif.getAllSuccessorEdges().isEmpty()) {
+ StatEdge ifedge = ifstat.getAllSuccessorEdges().get(0);
+ StatEdge edge = new StatEdge(ifedge.getType(), stif, ifedge.getDestination());
+
+ stif.addSuccessor(edge);
+ if (ifedge.closure != null) {
+ ifedge.closure.addLabeledEdge(edge);
+ }
+ }
+
+ SequenceHelper.destroyAndFlattenStatement(stif);
+
+ return true;
+ }
+ }
+ }
+ }
+ else if (ifexpr.type == Exprent.EXPRENT_EXIT && elseexpr.type == Exprent.EXPRENT_EXIT) {
+ ExitExprent ifex = (ExitExprent)ifexpr;
+ ExitExprent elseex = (ExitExprent)elseexpr;
+
+ if (ifex.getExittype() == elseex.getExittype() && ifex.getValue() != null && elseex.getValue() != null &&
+ ifex.getExittype() == ExitExprent.EXIT_RETURN) {
+
+ // throw is dangerous, because of implicit casting to a common superclass
+ // e.g. throws IOException and throw true?new RuntimeException():new IOException(); won't work
+ if (ifex.getExittype() == ExitExprent.EXIT_THROW &&
+ !ifex.getValue().getExprType().equals(elseex.getValue().getExprType())) { // note: getExprType unreliable at this point!
+ return false;
+ }
+
+ List<Exprent> data = new ArrayList<Exprent>();
+ data.addAll(stif.getFirst().getExprents());
+
+ data.add(new ExitExprent(ifex.getExittype(), new FunctionExprent(FunctionExprent.FUNCTION_IIF,
+ Arrays.asList(new Exprent[]{
+ stif.getHeadexprent().getCondition(),
+ ifex.getValue(),
+ elseex.getValue()})), ifex.getRettype()));
+ stif.setExprents(data);
+
+ StatEdge retedge = ifstat.getAllSuccessorEdges().get(0);
+ stif.addSuccessor(new StatEdge(StatEdge.TYPE_BREAK, stif, retedge.getDestination(),
+ retedge.closure == stif ? stif.getParent() : retedge.closure));
+
+ SequenceHelper.destroyAndFlattenStatement(stif);
+
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/StackVarsProcessor.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/StackVarsProcessor.java
new file mode 100644
index 000000000000..109490cc8ef9
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/StackVarsProcessor.java
@@ -0,0 +1,735 @@
+/*
+ * 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.modules.decompiler;
+
+import org.jetbrains.java.decompiler.code.CodeConstants;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
+import org.jetbrains.java.decompiler.modules.decompiler.sforms.*;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionEdge;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionNode;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionsGraph;
+import org.jetbrains.java.decompiler.struct.StructClass;
+import org.jetbrains.java.decompiler.struct.StructMethod;
+import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+import org.jetbrains.java.decompiler.util.SFormsFastMapDirect;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+
+public class StackVarsProcessor {
+
+ public void simplifyStackVars(RootStatement root, StructMethod mt, StructClass cl) {
+
+ HashSet<Integer> setReorderedIfs = new HashSet<Integer>();
+
+ SSAUConstructorSparseEx ssau = null;
+
+ while (true) {
+
+ boolean found = false;
+
+ // System.out.println("--------------- \r\n"+root.toJava());
+
+ SSAConstructorSparseEx ssa = new SSAConstructorSparseEx();
+ ssa.splitVariables(root, mt);
+
+ // System.out.println("--------------- \r\n"+root.toJava());
+
+
+ SimplifyExprentsHelper sehelper = new SimplifyExprentsHelper(ssau == null);
+ while (sehelper.simplifyStackVarsStatement(root, setReorderedIfs, ssa, cl)) {
+ // System.out.println("--------------- \r\n"+root.toJava());
+ found = true;
+ }
+
+
+ // System.out.println("=============== \r\n"+root.toJava());
+
+ setVersionsToNull(root);
+
+ SequenceHelper.condenseSequences(root);
+
+ ssau = new SSAUConstructorSparseEx();
+ ssau.splitVariables(root, mt);
+
+ // try {
+ // DotExporter.toDotFile(ssau.getSsuversions(), new File("c:\\Temp\\gr12_my.dot"));
+ // } catch(Exception ex) {
+ // ex.printStackTrace();
+ // }
+
+ // System.out.println("++++++++++++++++ \r\n"+root.toJava());
+
+
+ if (iterateStatements(root, ssau)) {
+ found = true;
+ }
+
+ // System.out.println("***************** \r\n"+root.toJava());
+
+ setVersionsToNull(root);
+
+ if (!found) {
+ break;
+ }
+ }
+
+ // remove unused assignments
+ ssau = new SSAUConstructorSparseEx();
+ ssau.splitVariables(root, mt);
+
+ // try {
+ // DotExporter.toDotFile(ssau.getSsuversions(), new File("c:\\Temp\\gr12_my.dot"));
+ // } catch(Exception ex) {
+ // ex.printStackTrace();
+ // }
+
+ iterateStatements(root, ssau);
+
+ // System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
+
+ setVersionsToNull(root);
+ }
+
+ private static void setVersionsToNull(Statement stat) {
+
+ if (stat.getExprents() == null) {
+ for (Object obj : stat.getSequentialObjects()) {
+ if (obj instanceof Statement) {
+ setVersionsToNull((Statement)obj);
+ }
+ else if (obj instanceof Exprent) {
+ setExprentVersionsToNull((Exprent)obj);
+ }
+ }
+ }
+ else {
+ for (Exprent exprent : stat.getExprents()) {
+ setExprentVersionsToNull(exprent);
+ }
+ }
+ }
+
+ private static void setExprentVersionsToNull(Exprent exprent) {
+
+ List<Exprent> lst = exprent.getAllExprents(true);
+ lst.add(exprent);
+
+ for (Exprent expr : lst) {
+ if (expr.type == Exprent.EXPRENT_VAR) {
+ ((VarExprent)expr).setVersion(0);
+ }
+ }
+ }
+
+
+ private boolean iterateStatements(RootStatement root, SSAUConstructorSparseEx ssa) {
+
+ FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();
+ DirectGraph dgraph = flatthelper.buildDirectGraph(root);
+
+ boolean res = false;
+
+ HashSet<DirectNode> setVisited = new HashSet<DirectNode>();
+ LinkedList<DirectNode> stack = new LinkedList<DirectNode>();
+ LinkedList<HashMap<VarVersionPaar, Exprent>> stackMaps = new LinkedList<HashMap<VarVersionPaar, Exprent>>();
+
+ stack.add(dgraph.first);
+ stackMaps.add(new HashMap<VarVersionPaar, Exprent>());
+
+ while (!stack.isEmpty()) {
+
+ DirectNode nd = stack.removeFirst();
+ HashMap<VarVersionPaar, Exprent> mapVarValues = stackMaps.removeFirst();
+
+ if (setVisited.contains(nd)) {
+ continue;
+ }
+ setVisited.add(nd);
+
+ List<List<Exprent>> lstLists = new ArrayList<List<Exprent>>();
+
+ if (!nd.exprents.isEmpty()) {
+ lstLists.add(nd.exprents);
+ }
+
+ if (nd.succs.size() == 1) {
+ DirectNode ndsucc = nd.succs.get(0);
+ if (ndsucc.type == DirectNode.NODE_TAIL && !ndsucc.exprents.isEmpty()) {
+ lstLists.add(nd.succs.get(0).exprents);
+ nd = ndsucc;
+ }
+ }
+
+ for (int i = 0; i < lstLists.size(); i++) {
+ List<Exprent> lst = lstLists.get(i);
+
+ int index = 0;
+ while (index < lst.size()) {
+ Exprent next = null;
+ if (index == lst.size() - 1) {
+ if (i < lstLists.size() - 1) {
+ next = lstLists.get(i + 1).get(0);
+ }
+ }
+ else {
+ next = lst.get(index + 1);
+ }
+
+ int[] ret = iterateExprent(lst, index, next, mapVarValues, ssa);
+
+ //System.out.println("***************** \r\n"+root.toJava());
+
+ if (ret[0] >= 0) {
+ index = ret[0];
+ }
+ else {
+ index++;
+ }
+ res |= (ret[1] == 1);
+ }
+ }
+
+ for (DirectNode ndx : nd.succs) {
+ stack.add(ndx);
+ stackMaps.add(new HashMap<VarVersionPaar, Exprent>(mapVarValues));
+ }
+
+ // make sure the 3 special exprent lists in a loop (init, condition, increment) are not empty
+ // change loop type if necessary
+ if (nd.exprents.isEmpty() &&
+ (nd.type == DirectNode.NODE_INIT || nd.type == DirectNode.NODE_CONDITION || nd.type == DirectNode.NODE_INCREMENT)) {
+ nd.exprents.add(null);
+
+ if (nd.statement.type == Statement.TYPE_DO) {
+ DoStatement loop = (DoStatement)nd.statement;
+
+ if (loop.getLooptype() == DoStatement.LOOP_FOR &&
+ loop.getInitExprent() == null &&
+ loop.getIncExprent() == null) { // "downgrade" loop to 'while'
+ loop.setLooptype(DoStatement.LOOP_WHILE);
+ }
+ }
+ }
+ }
+
+ return res;
+ }
+
+
+ private static Exprent isReplaceableVar(Exprent exprent, HashMap<VarVersionPaar, Exprent> mapVarValues, SSAUConstructorSparseEx ssau) {
+
+ Exprent dest = null;
+
+ if (exprent.type == Exprent.EXPRENT_VAR) {
+ VarExprent var = (VarExprent)exprent;
+ dest = mapVarValues.get(new VarVersionPaar(var));
+ }
+
+ return dest;
+ }
+
+ private static void replaceSingleVar(Exprent parent, VarExprent var, Exprent dest, SSAUConstructorSparseEx ssau) {
+
+ parent.replaceExprent(var, dest);
+
+ // live sets
+ SFormsFastMapDirect livemap = ssau.getLiveVarVersionsMap(new VarVersionPaar(var));
+ HashSet<VarVersionPaar> setVars = getAllVersions(dest);
+
+ for (VarVersionPaar varpaar : setVars) {
+ VarVersionNode node = ssau.getSsuversions().nodes.getWithKey(varpaar);
+
+ for (Iterator<Entry<Integer, FastSparseSet<Integer>>> itent = node.live.entryList().iterator(); itent.hasNext(); ) {
+ Entry<Integer, FastSparseSet<Integer>> ent = itent.next();
+
+ Integer key = ent.getKey();
+
+ if (!livemap.containsKey(key)) {
+ itent.remove();
+ }
+ else {
+ FastSparseSet<Integer> set = ent.getValue();
+
+ set.complement(livemap.get(key));
+ if (set.isEmpty()) {
+ itent.remove();
+ }
+ }
+ }
+ }
+ }
+
+ private int[] iterateExprent(List<Exprent> lstExprents, int index, Exprent next, HashMap<VarVersionPaar,
+ Exprent> mapVarValues, SSAUConstructorSparseEx ssau) {
+
+ Exprent exprent = lstExprents.get(index);
+
+ int changed = 0;
+
+ for (Exprent expr : exprent.getAllExprents()) {
+ while (true) {
+ Object[] arr = iterateChildExprent(expr, exprent, next, mapVarValues, ssau);
+ Exprent retexpr = (Exprent)arr[0];
+ changed |= (Boolean)arr[1] ? 1 : 0;
+
+ boolean isReplaceable = (Boolean)arr[2];
+ if (retexpr != null) {
+ if (isReplaceable) {
+ replaceSingleVar(exprent, (VarExprent)expr, retexpr, ssau);
+ expr = retexpr;
+ }
+ else {
+ exprent.replaceExprent(expr, retexpr);
+ }
+ changed = 1;
+ }
+
+ if (!isReplaceable) {
+ break;
+ }
+ }
+ }
+
+ // no var on the highest level, so no replacing
+
+ VarExprent left = null;
+ Exprent right = null;
+
+ if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
+ AssignmentExprent as = (AssignmentExprent)exprent;
+ if (as.getLeft().type == Exprent.EXPRENT_VAR) {
+ left = (VarExprent)as.getLeft();
+ right = as.getRight();
+ }
+ }
+
+ if (left == null) {
+ return new int[]{-1, changed};
+ }
+
+ VarVersionPaar leftpaar = new VarVersionPaar(left);
+
+ List<VarVersionNode> usedVers = new ArrayList<VarVersionNode>();
+ boolean notdom = getUsedVersions(ssau, leftpaar, usedVers);
+
+ if (!notdom && usedVers.isEmpty()) {
+ if (left.isStack() && (right.type == Exprent.EXPRENT_INVOCATION ||
+ right.type == Exprent.EXPRENT_ASSIGNMENT || right.type == Exprent.EXPRENT_NEW)) {
+ if (right.type == Exprent.EXPRENT_NEW) {
+ // new Object(); permitted
+ NewExprent nexpr = (NewExprent)right;
+ if (nexpr.isAnonymous() || nexpr.getNewtype().arraydim > 0
+ || nexpr.getNewtype().type != CodeConstants.TYPE_OBJECT) {
+ return new int[]{-1, changed};
+ }
+ }
+
+ lstExprents.set(index, right);
+ return new int[]{index + 1, 1};
+ }
+ else if (right.type == Exprent.EXPRENT_VAR) {
+ lstExprents.remove(index);
+ return new int[]{index, 1};
+ }
+ else {
+ return new int[]{-1, changed};
+ }
+ }
+
+ int useflags = right.getExprentUse();
+
+ // stack variables only
+ if (!left.isStack() &&
+ (right.type != Exprent.EXPRENT_VAR || ((VarExprent)right).isStack())) { // special case catch(... ex)
+ return new int[]{-1, changed};
+ }
+
+ if ((useflags & Exprent.MULTIPLE_USES) == 0 && (notdom || usedVers.size() > 1)) {
+ return new int[]{-1, changed};
+ }
+
+ HashMap<Integer, HashSet<VarVersionPaar>> mapVars = getAllVarVersions(leftpaar, right, ssau);
+
+ boolean isSelfReference = mapVars.containsKey(leftpaar.var);
+ if (isSelfReference && notdom) {
+ return new int[]{-1, changed};
+ }
+
+ HashSet<VarVersionPaar> setNextVars = next == null ? null : getAllVersions(next);
+
+ // FIXME: fix the entire method!
+ if (right.type != Exprent.EXPRENT_CONST &&
+ right.type != Exprent.EXPRENT_VAR &&
+ setNextVars != null &&
+ mapVars.containsKey(leftpaar.var)) {
+ for (VarVersionNode usedvar : usedVers) {
+ if (!setNextVars.contains(new VarVersionPaar(usedvar.var, usedvar.version))) {
+ return new int[]{-1, changed};
+ }
+ }
+ }
+
+ mapVars.remove(leftpaar.var);
+
+ boolean vernotreplaced = false;
+ boolean verreplaced = false;
+
+
+ HashSet<VarVersionPaar> setTempUsedVers = new HashSet<VarVersionPaar>();
+
+ for (VarVersionNode usedvar : usedVers) {
+ VarVersionPaar usedver = new VarVersionPaar(usedvar.var, usedvar.version);
+ if (isVersionToBeReplaced(usedver, mapVars, ssau, leftpaar) &&
+ (right.type == Exprent.EXPRENT_CONST || right.type == Exprent.EXPRENT_VAR || right.type == Exprent.EXPRENT_FIELD
+ || setNextVars == null || setNextVars.contains(usedver))) {
+
+ setTempUsedVers.add(usedver);
+ verreplaced = true;
+ }
+ else {
+ vernotreplaced = true;
+ }
+ }
+
+ if (isSelfReference && vernotreplaced) {
+ return new int[]{-1, changed};
+ }
+ else {
+ for (VarVersionPaar usedver : setTempUsedVers) {
+ Exprent copy = right.copy();
+ if (right.type == Exprent.EXPRENT_FIELD && ssau.getMapFieldVars().containsKey(right.id)) {
+ ssau.getMapFieldVars().put(copy.id, ssau.getMapFieldVars().get(right.id));
+ }
+
+ mapVarValues.put(usedver, copy);
+ }
+ }
+
+ if (!notdom && !vernotreplaced) {
+ // remove assignment
+ lstExprents.remove(index);
+ return new int[]{index, 1};
+ }
+ else if (verreplaced) {
+ return new int[]{index + 1, changed};
+ }
+ else {
+ return new int[]{-1, changed};
+ }
+ }
+
+ private static HashSet<VarVersionPaar> getAllVersions(Exprent exprent) {
+
+ HashSet<VarVersionPaar> res = new HashSet<VarVersionPaar>();
+
+ List<Exprent> listTemp = new ArrayList<Exprent>(exprent.getAllExprents(true));
+ listTemp.add(exprent);
+
+ for (Exprent expr : listTemp) {
+ if (expr.type == Exprent.EXPRENT_VAR) {
+ VarExprent var = (VarExprent)expr;
+ res.add(new VarVersionPaar(var));
+ }
+ }
+
+ return res;
+ }
+
+ private static Object[] iterateChildExprent(Exprent exprent,
+ Exprent parent,
+ Exprent next,
+ HashMap<VarVersionPaar, Exprent> mapVarValues,
+ SSAUConstructorSparseEx ssau) {
+
+ boolean changed = false;
+
+ for (Exprent expr : exprent.getAllExprents()) {
+ while (true) {
+ Object[] arr = iterateChildExprent(expr, parent, next, mapVarValues, ssau);
+ Exprent retexpr = (Exprent)arr[0];
+ changed |= (Boolean)arr[1];
+
+ boolean isReplaceable = (Boolean)arr[2];
+ if (retexpr != null) {
+ if (isReplaceable) {
+ replaceSingleVar(exprent, (VarExprent)expr, retexpr, ssau);
+ expr = retexpr;
+ }
+ else {
+ exprent.replaceExprent(expr, retexpr);
+ }
+ changed = true;
+ }
+
+ if (!isReplaceable) {
+ break;
+ }
+ }
+ }
+
+ Exprent dest = isReplaceableVar(exprent, mapVarValues, ssau);
+ if (dest != null) {
+ return new Object[]{dest, true, true};
+ }
+
+
+ VarExprent left = null;
+ Exprent right = null;
+
+ if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
+ AssignmentExprent as = (AssignmentExprent)exprent;
+ if (as.getLeft().type == Exprent.EXPRENT_VAR) {
+ left = (VarExprent)as.getLeft();
+ right = as.getRight();
+ }
+ }
+
+ if (left == null) {
+ return new Object[]{null, changed, false};
+ }
+
+ boolean isHeadSynchronized = false;
+ if (next == null && parent.type == Exprent.EXPRENT_MONITOR) {
+ MonitorExprent monexpr = (MonitorExprent)parent;
+ if (monexpr.getMontype() == MonitorExprent.MONITOR_ENTER && exprent.equals(monexpr.getValue())) {
+ isHeadSynchronized = true;
+ }
+ }
+
+ // stack variable or synchronized head exprent
+ if (!left.isStack() && !isHeadSynchronized) {
+ return new Object[]{null, changed, false};
+ }
+
+ VarVersionPaar leftpaar = new VarVersionPaar(left);
+
+ List<VarVersionNode> usedVers = new ArrayList<VarVersionNode>();
+ boolean notdom = getUsedVersions(ssau, leftpaar, usedVers);
+
+ if (!notdom && usedVers.isEmpty()) {
+ return new Object[]{right, changed, false};
+ }
+
+ // stack variables only
+ if (!left.isStack()) {
+ return new Object[]{null, changed, false};
+ }
+
+ int useflags = right.getExprentUse();
+
+ if ((useflags & Exprent.BOTH_FLAGS) != Exprent.BOTH_FLAGS) {
+ return new Object[]{null, changed, false};
+ }
+
+ HashMap<Integer, HashSet<VarVersionPaar>> mapVars = getAllVarVersions(leftpaar, right, ssau);
+
+ if (mapVars.containsKey(leftpaar.var) && notdom) {
+ return new Object[]{null, changed, false};
+ }
+
+
+ mapVars.remove(leftpaar.var);
+
+ HashSet<VarVersionPaar> setAllowedVars = getAllVersions(parent);
+ if (next != null) {
+ setAllowedVars.addAll(getAllVersions(next));
+ }
+
+ boolean vernotreplaced = false;
+
+ HashSet<VarVersionPaar> setTempUsedVers = new HashSet<VarVersionPaar>();
+
+ for (VarVersionNode usedvar : usedVers) {
+ VarVersionPaar usedver = new VarVersionPaar(usedvar.var, usedvar.version);
+ if (isVersionToBeReplaced(usedver, mapVars, ssau, leftpaar) &&
+ (right.type == Exprent.EXPRENT_VAR || setAllowedVars.contains(usedver))) {
+
+ setTempUsedVers.add(usedver);
+ }
+ else {
+ vernotreplaced = true;
+ }
+ }
+
+ if (!notdom && !vernotreplaced) {
+
+ for (VarVersionPaar usedver : setTempUsedVers) {
+ Exprent copy = right.copy();
+ if (right.type == Exprent.EXPRENT_FIELD && ssau.getMapFieldVars().containsKey(right.id)) {
+ ssau.getMapFieldVars().put(copy.id, ssau.getMapFieldVars().get(right.id));
+ }
+
+ mapVarValues.put(usedver, copy);
+ }
+
+ // remove assignment
+ return new Object[]{right, changed, false};
+ }
+
+ return new Object[]{null, changed, false};
+ }
+
+ private static boolean getUsedVersions(SSAUConstructorSparseEx ssa, VarVersionPaar var, List<VarVersionNode> res) {
+
+ VarVersionsGraph ssuversions = ssa.getSsuversions();
+ VarVersionNode varnode = ssuversions.nodes.getWithKey(var);
+
+ HashSet<VarVersionNode> setVisited = new HashSet<VarVersionNode>();
+
+ HashSet<VarVersionNode> setNotDoms = new HashSet<VarVersionNode>();
+
+ LinkedList<VarVersionNode> stack = new LinkedList<VarVersionNode>();
+ stack.add(varnode);
+
+ while (!stack.isEmpty()) {
+
+ VarVersionNode nd = stack.remove(0);
+ setVisited.add(nd);
+
+ if (nd != varnode && (nd.flags & VarVersionNode.FLAG_PHANTOM_FINEXIT) == 0) {
+ res.add(nd);
+ }
+
+ for (VarVersionEdge edge : nd.succs) {
+ VarVersionNode succ = edge.dest;
+
+ if (!setVisited.contains(edge.dest)) {
+
+ boolean isDominated = true;
+ for (VarVersionEdge prededge : succ.preds) {
+ if (!setVisited.contains(prededge.source)) {
+ isDominated = false;
+ break;
+ }
+ }
+
+ if (isDominated) {
+ stack.add(succ);
+ }
+ else {
+ setNotDoms.add(succ);
+ }
+ }
+ }
+ }
+
+ setNotDoms.removeAll(setVisited);
+
+ return !setNotDoms.isEmpty();
+ }
+
+ private static boolean isVersionToBeReplaced(VarVersionPaar usedvar,
+ HashMap<Integer, HashSet<VarVersionPaar>> mapVars,
+ SSAUConstructorSparseEx ssau,
+ VarVersionPaar leftpaar) {
+
+ VarVersionsGraph ssuversions = ssau.getSsuversions();
+
+ SFormsFastMapDirect mapLiveVars = ssau.getLiveVarVersionsMap(usedvar);
+ if (mapLiveVars == null) {
+ // dummy version, predecessor of a phi node
+ return false;
+ }
+
+ // compare protected ranges
+ if (!InterpreterUtil.equalObjects(ssau.getMapVersionFirstRange().get(leftpaar),
+ ssau.getMapVersionFirstRange().get(usedvar))) {
+ return false;
+ }
+
+ for (Entry<Integer, HashSet<VarVersionPaar>> ent : mapVars.entrySet()) {
+ FastSparseSet<Integer> liveverset = mapLiveVars.get(ent.getKey());
+ if (liveverset == null) {
+ return false;
+ }
+
+ HashSet<VarVersionNode> domset = new HashSet<VarVersionNode>();
+ for (VarVersionPaar verpaar : ent.getValue()) {
+ domset.add(ssuversions.nodes.getWithKey(verpaar));
+ }
+
+ boolean isdom = false;
+
+ for (Integer livever : liveverset) {
+ VarVersionNode node = ssuversions.nodes.getWithKey(new VarVersionPaar(ent.getKey().intValue(), livever.intValue()));
+
+ if (ssuversions.isDominatorSet(node, domset)) {
+ isdom = true;
+ break;
+ }
+ }
+
+ if (!isdom) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static HashMap<Integer, HashSet<VarVersionPaar>> getAllVarVersions(VarVersionPaar leftvar,
+ Exprent exprent,
+ SSAUConstructorSparseEx ssau) {
+
+ HashMap<Integer, HashSet<VarVersionPaar>> map = new HashMap<Integer, HashSet<VarVersionPaar>>();
+ SFormsFastMapDirect mapLiveVars = ssau.getLiveVarVersionsMap(leftvar);
+
+ List<Exprent> lst = exprent.getAllExprents(true);
+ lst.add(exprent);
+
+ for (Exprent expr : lst) {
+ if (expr.type == Exprent.EXPRENT_VAR) {
+ int varindex = ((VarExprent)expr).getIndex();
+ if (leftvar.var != varindex) {
+ if (mapLiveVars.containsKey(varindex)) {
+ HashSet<VarVersionPaar> verset = new HashSet<VarVersionPaar>();
+ for (Integer vers : mapLiveVars.get(varindex)) {
+ verset.add(new VarVersionPaar(varindex, vers.intValue()));
+ }
+ map.put(varindex, verset);
+ }
+ else {
+ throw new RuntimeException("inkonsistent live map!");
+ }
+ }
+ else {
+ map.put(varindex, null);
+ }
+ }
+ else if (expr.type == Exprent.EXPRENT_FIELD) {
+ if (ssau.getMapFieldVars().containsKey(expr.id)) {
+ int varindex = ssau.getMapFieldVars().get(expr.id);
+ if (mapLiveVars.containsKey(varindex)) {
+ HashSet<VarVersionPaar> verset = new HashSet<VarVersionPaar>();
+ for (Integer vers : mapLiveVars.get(varindex)) {
+ verset.add(new VarVersionPaar(varindex, vers.intValue()));
+ }
+ map.put(varindex, verset);
+ }
+ }
+ }
+ }
+
+ return map;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/StatEdge.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/StatEdge.java
new file mode 100644
index 000000000000..26d427b93770
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/StatEdge.java
@@ -0,0 +1,104 @@
+/*
+ * 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.modules.decompiler;
+
+import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class StatEdge {
+
+ public static final int TYPE_ALL = 0xFF;
+
+ public static final int TYPE_REGULAR = 1;
+ public static final int TYPE_EXCEPTION = 2;
+ public static final int TYPE_BREAK = 4;
+ public static final int TYPE_CONTINUE = 8;
+ public static final int TYPE_FINALLYEXIT = 32;
+
+ public static final int[] TYPES = new int[]{
+ TYPE_REGULAR,
+ TYPE_EXCEPTION,
+ TYPE_BREAK,
+ TYPE_CONTINUE,
+ TYPE_FINALLYEXIT
+ };
+
+ private int type;
+
+ private Statement source;
+
+ private Statement destination;
+
+ private List<String> exceptions;
+
+ public Statement closure;
+
+ public boolean labeled = true;
+
+ public boolean explicit = true;
+
+ public StatEdge(int type, Statement source, Statement destination, Statement closure) {
+ this(type, source, destination);
+ this.closure = closure;
+ }
+
+ public StatEdge(int type, Statement source, Statement destination) {
+ this.type = type;
+ this.source = source;
+ this.destination = destination;
+ }
+
+ public StatEdge(Statement source, Statement destination, List<String> exceptions) {
+ this(TYPE_EXCEPTION, source, destination);
+ if (exceptions != null) {
+ this.exceptions = new ArrayList<String>(exceptions);
+ }
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public void setType(int type) {
+ this.type = type;
+ }
+
+ public Statement getSource() {
+ return source;
+ }
+
+ public void setSource(Statement source) {
+ this.source = source;
+ }
+
+ public Statement getDestination() {
+ return destination;
+ }
+
+ public void setDestination(Statement destination) {
+ this.destination = destination;
+ }
+
+ public List<String> getExceptions() {
+ return this.exceptions;
+ }
+
+ // public void setException(String exception) {
+ // this.exception = exception;
+ // }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java
new file mode 100644
index 000000000000..23f778daea97
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java
@@ -0,0 +1,207 @@
+/*
+ * 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.modules.decompiler;
+
+import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
+import org.jetbrains.java.decompiler.util.ListStack;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
+// --------------------------------------------------------------------
+// Algorithm
+// --------------------------------------------------------------------
+// DFS(G)
+// {
+// make a new vertex x with edges x->v for all v
+// initialize a counter N to zero
+// initialize list L to empty
+// build directed tree T, initially a single vertex {x}
+// visit(x)
+// }
+//
+// visit(p)
+// {
+// add p to L
+// dfsnum(p) = N
+// increment N
+// low(p) = dfsnum(p)
+// for each edge p->q
+// if q is not already in T
+// {
+// add p->q to T
+// visit(q)
+// low(p) = min(low(p), low(q))
+// } else low(p) = min(low(p), dfsnum(q))
+//
+// if low(p)=dfsnum(p)
+// {
+// output "component:"
+// repeat
+// remove last element v from L
+// output v
+// remove v from G
+// until v=p
+// }
+// }
+// --------------------------------------------------------------------
+
+public class StrongConnectivityHelper {
+
+ private ListStack<Statement> lstack;
+
+ private int ncounter;
+
+ private HashSet<Statement> tset;
+ private HashMap<Statement, Integer> dfsnummap;
+ private HashMap<Statement, Integer> lowmap;
+
+ private List<List<Statement>> components;
+
+ private HashSet<Statement> setProcessed;
+
+ // *****************************************************************************
+ // constructors
+ // *****************************************************************************
+
+ public StrongConnectivityHelper() {
+ }
+
+ public StrongConnectivityHelper(Statement stat) {
+ findComponents(stat);
+ }
+
+ // *****************************************************************************
+ // public methods
+ // *****************************************************************************
+
+ public List<List<Statement>> findComponents(Statement stat) {
+
+ components = new ArrayList<List<Statement>>();
+ setProcessed = new HashSet<Statement>();
+
+ visitTree(stat.getFirst());
+
+ for (Statement st : stat.getStats()) {
+ if (!setProcessed.contains(st) && st.getPredecessorEdges(Statement.STATEDGE_DIRECT_ALL).isEmpty()) {
+ visitTree(st);
+ }
+ }
+
+ // should not find any more nodes! FIXME: ??
+ for (Statement st : stat.getStats()) {
+ if (!setProcessed.contains(st)) {
+ visitTree(st);
+ }
+ }
+
+ return components;
+ }
+
+ public static boolean isExitComponent(List<Statement> lst) {
+
+ HashSet<Statement> set = new HashSet<Statement>();
+ for (Statement stat : lst) {
+ set.addAll(stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD));
+ }
+ set.removeAll(lst);
+
+ return (set.size() == 0);
+ }
+
+ public static List<Statement> getExitReps(List<List<Statement>> lst) {
+
+ List<Statement> res = new ArrayList<Statement>();
+
+ for (List<Statement> comp : lst) {
+ if (isExitComponent(comp)) {
+ res.add(comp.get(0));
+ }
+ }
+
+ return res;
+ }
+
+ // *****************************************************************************
+ // private methods
+ // *****************************************************************************
+
+ private void visitTree(Statement stat) {
+ lstack = new ListStack<Statement>();
+ ncounter = 0;
+ tset = new HashSet<Statement>();
+ dfsnummap = new HashMap<Statement, Integer>();
+ lowmap = new HashMap<Statement, Integer>();
+
+ visit(stat);
+
+ setProcessed.addAll(tset);
+ setProcessed.add(stat);
+ }
+
+ private void visit(Statement stat) {
+
+ lstack.push(stat);
+ dfsnummap.put(stat, ncounter);
+ lowmap.put(stat, ncounter);
+ ncounter++;
+
+ List<Statement> lstSuccs = stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD); // TODO: set?
+ lstSuccs.removeAll(setProcessed);
+
+ for (int i = 0; i < lstSuccs.size(); i++) {
+ Statement succ = lstSuccs.get(i);
+ int secvalue;
+
+ if (tset.contains(succ)) {
+ secvalue = dfsnummap.get(succ);
+ }
+ else {
+ tset.add(succ);
+ visit(succ);
+ secvalue = lowmap.get(succ);
+ }
+ lowmap.put(stat, Math.min(lowmap.get(stat), secvalue));
+ }
+
+
+ if (lowmap.get(stat).intValue() == dfsnummap.get(stat).intValue()) {
+ List<Statement> lst = new ArrayList<Statement>();
+ Statement v;
+ do {
+ v = lstack.pop();
+ lst.add(v);
+ }
+ while (v != stat);
+ components.add(lst);
+ }
+ }
+
+
+ // *****************************************************************************
+ // getter and setter methods
+ // *****************************************************************************
+
+ public List<List<Statement>> getComponents() {
+ return components;
+ }
+
+ public void setComponents(List<List<Statement>> components) {
+ this.components = components;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorEngine.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorEngine.java
new file mode 100644
index 000000000000..aeab0d2cf948
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorEngine.java
@@ -0,0 +1,128 @@
+/*
+ * 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.modules.decompiler.decompose;
+
+import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
+import org.jetbrains.java.decompiler.util.VBStyleCollection;
+
+import java.util.List;
+
+public class DominatorEngine {
+
+ private Statement statement;
+
+ private VBStyleCollection<Integer, Integer> colOrderedIDoms = new VBStyleCollection<Integer, Integer>();
+
+
+ public DominatorEngine(Statement statement) {
+ this.statement = statement;
+ }
+
+ public void initialize() {
+ calcIDoms();
+ }
+
+ private void orderStatements() {
+
+ for (Statement stat : statement.getReversePostOrderList()) {
+ colOrderedIDoms.addWithKey(null, stat.id);
+ }
+ }
+
+ private static Integer getCommonIDom(Integer key1, Integer key2, VBStyleCollection<Integer, Integer> orderedIDoms) {
+
+ if (key1 == null) {
+ return key2;
+ }
+ else if (key2 == null) {
+ return key1;
+ }
+
+ int index1 = orderedIDoms.getIndexByKey(key1);
+ int index2 = orderedIDoms.getIndexByKey(key2);
+
+ while (index1 != index2) {
+ if (index1 > index2) {
+ key1 = orderedIDoms.getWithKey(key1);
+ index1 = orderedIDoms.getIndexByKey(key1);
+ }
+ else {
+ key2 = orderedIDoms.getWithKey(key2);
+ index2 = orderedIDoms.getIndexByKey(key2);
+ }
+ }
+
+ return key1;
+ }
+
+ private void calcIDoms() {
+
+ orderStatements();
+
+ colOrderedIDoms.putWithKey(statement.getFirst().id, statement.getFirst().id);
+
+ // exclude first statement
+ List<Integer> lstIds = colOrderedIDoms.getLstKeys().subList(1, colOrderedIDoms.getLstKeys().size());
+
+ while (true) {
+
+ boolean changed = false;
+
+ for (Integer id : lstIds) {
+
+ Statement stat = statement.getStats().getWithKey(id);
+ Integer idom = null;
+
+ for (StatEdge edge : stat.getAllPredecessorEdges()) {
+ if (colOrderedIDoms.getWithKey(edge.getSource().id) != null) {
+ idom = getCommonIDom(idom, edge.getSource().id, colOrderedIDoms);
+ }
+ }
+
+ Integer oldidom = colOrderedIDoms.putWithKey(idom, id);
+ if (!idom.equals(oldidom)) {
+ changed = true;
+ }
+ }
+
+ if (!changed) {
+ break;
+ }
+ }
+ }
+
+ public VBStyleCollection<Integer, Integer> getOrderedIDoms() {
+ return colOrderedIDoms;
+ }
+
+ public boolean isDominator(Integer node, Integer dom) {
+
+ while (!node.equals(dom)) {
+
+ Integer idom = colOrderedIDoms.getWithKey(node);
+
+ if (idom.equals(node)) {
+ return false; // root node
+ }
+ else {
+ node = idom;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorTreeExceptionFilter.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorTreeExceptionFilter.java
new file mode 100644
index 000000000000..c11d5bf0c67e
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorTreeExceptionFilter.java
@@ -0,0 +1,184 @@
+/*
+ * 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.modules.decompiler.decompose;
+
+import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
+import org.jetbrains.java.decompiler.util.VBStyleCollection;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+public class DominatorTreeExceptionFilter {
+
+ private Statement statement;
+
+ // idom, nodes
+ private Map<Integer, Set<Integer>> mapTreeBranches = new HashMap<Integer, Set<Integer>>();
+
+ // handler, range nodes
+ private Map<Integer, Set<Integer>> mapExceptionRanges = new HashMap<Integer, Set<Integer>>();
+
+ // handler, head dom
+ private Map<Integer, Integer> mapExceptionDoms = new HashMap<Integer, Integer>();
+
+ // statement, handler, exit nodes
+ private Map<Integer, Map<Integer, Integer>> mapExceptionRangeUniqueExit = new HashMap<Integer, Map<Integer, Integer>>();
+
+ private DominatorEngine domEngine;
+
+ public DominatorTreeExceptionFilter(Statement statement) {
+ this.statement = statement;
+ }
+
+ public void initialize() {
+
+ domEngine = new DominatorEngine(statement);
+ domEngine.initialize();
+
+ buildDominatorTree();
+
+ buildExceptionRanges();
+
+ buildFilter(statement.getFirst().id);
+
+ // free resources
+ mapTreeBranches.clear();
+ mapExceptionRanges.clear();
+ }
+
+ public boolean acceptStatementPair(Integer head, Integer exit) {
+
+ Map<Integer, Integer> filter = mapExceptionRangeUniqueExit.get(head);
+ for (Entry<Integer, Integer> entry : filter.entrySet()) {
+ if (!head.equals(mapExceptionDoms.get(entry.getKey()))) {
+ Integer filterExit = entry.getValue();
+ if (filterExit.intValue() == -1 || !filterExit.equals(exit)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private void buildDominatorTree() {
+
+ VBStyleCollection<Integer, Integer> orderedIDoms = domEngine.getOrderedIDoms();
+
+ List<Integer> lstKeys = orderedIDoms.getLstKeys();
+ for (int index = lstKeys.size() - 1; index >= 0; index--) {
+ Integer key = lstKeys.get(index);
+ Integer idom = orderedIDoms.get(index);
+
+ Set<Integer> set = mapTreeBranches.get(idom);
+ if (set == null) {
+ mapTreeBranches.put(idom, set = new HashSet<Integer>());
+ }
+ set.add(key);
+ }
+
+ Integer firstid = statement.getFirst().id;
+ mapTreeBranches.get(firstid).remove(firstid);
+ }
+
+ private void buildExceptionRanges() {
+
+ for (Statement stat : statement.getStats()) {
+ List<Statement> lstPreds = stat.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_BACKWARD);
+ if (!lstPreds.isEmpty()) {
+
+ Set<Integer> set = new HashSet<Integer>();
+
+ for (Statement st : lstPreds) {
+ set.add(st.id);
+ }
+
+ mapExceptionRanges.put(stat.id, set);
+ }
+ }
+
+ mapExceptionDoms = buildExceptionDoms(statement.getFirst().id);
+ }
+
+ private Map<Integer, Integer> buildExceptionDoms(Integer id) {
+
+ Map<Integer, Integer> map = new HashMap<Integer, Integer>();
+
+ Set<Integer> children = mapTreeBranches.get(id);
+ if (children != null) {
+ for (Integer childid : children) {
+ Map<Integer, Integer> mapChild = buildExceptionDoms(childid);
+ for (Integer handler : mapChild.keySet()) {
+ map.put(handler, map.containsKey(handler) ? id : mapChild.get(handler));
+ }
+ }
+ }
+
+ for (Entry<Integer, Set<Integer>> entry : mapExceptionRanges.entrySet()) {
+ if (entry.getValue().contains(id)) {
+ map.put(entry.getKey(), id);
+ }
+ }
+
+ return map;
+ }
+
+
+ private void buildFilter(Integer id) {
+
+ Map<Integer, Integer> map = new HashMap<Integer, Integer>();
+
+ Set<Integer> children = mapTreeBranches.get(id);
+ if (children != null) {
+ for (Integer childid : children) {
+
+ buildFilter(childid);
+
+ Map<Integer, Integer> mapChild = mapExceptionRangeUniqueExit.get(childid);
+
+ for (Entry<Integer, Set<Integer>> entry : mapExceptionRanges.entrySet()) {
+
+ Integer handler = entry.getKey();
+ Set<Integer> range = entry.getValue();
+
+ if (range.contains(id)) {
+
+ Integer exit = null;
+
+ if (!range.contains(childid)) {
+ exit = childid;
+ }
+ else {
+ // exit = map.containsKey(handler)?-1:mapChild.get(handler); FIXME: Eclipse bug?
+ exit = map.containsKey(handler) ? new Integer(-1) : mapChild.get(handler);
+ }
+
+ if (exit != null) {
+ map.put(handler, exit);
+ }
+ }
+ }
+ }
+ }
+
+ mapExceptionRangeUniqueExit.put(id, map);
+ }
+
+ public DominatorEngine getDomEngine() {
+ return domEngine;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/FastExtendedPostdominanceHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/FastExtendedPostdominanceHelper.java
new file mode 100644
index 000000000000..f029016e5efe
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/FastExtendedPostdominanceHelper.java
@@ -0,0 +1,355 @@
+/*
+ * 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.modules.decompiler.decompose;
+
+import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
+import org.jetbrains.java.decompiler.util.FastFixedSetFactory;
+import org.jetbrains.java.decompiler.util.FastFixedSetFactory.FastFixedSet;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+public class FastExtendedPostdominanceHelper {
+
+ private List<Statement> lstReversePostOrderList;
+
+ private HashMap<Integer, FastFixedSet<Integer>> mapSupportPoints = new HashMap<Integer, FastFixedSet<Integer>>();
+
+ private HashMap<Integer, FastFixedSet<Integer>> mapExtPostdominators = new HashMap<Integer, FastFixedSet<Integer>>();
+
+ private Statement statement;
+
+ private FastFixedSetFactory<Integer> factory;
+
+ public HashMap<Integer, Set<Integer>> getExtendedPostdominators(Statement statement) {
+
+ this.statement = statement;
+
+ HashSet<Integer> set = new HashSet<Integer>();
+ for (Statement st : statement.getStats()) {
+ set.add(st.id);
+ }
+ this.factory = new FastFixedSetFactory<Integer>(set);
+
+ lstReversePostOrderList = statement.getReversePostOrderList();
+
+ // try {
+ // DotExporter.toDotFile(statement, new File("c:\\Temp\\stat1.dot"));
+ // } catch (Exception ex) {
+ // ex.printStackTrace();
+ // }
+
+ calcDefaultReachableSets();
+
+ removeErroneousNodes();
+
+ DominatorTreeExceptionFilter filter = new DominatorTreeExceptionFilter(statement);
+ filter.initialize();
+
+ filterOnExceptionRanges(filter);
+
+ filterOnDominance(filter);
+
+ HashMap<Integer, Set<Integer>> res = new HashMap<Integer, Set<Integer>>();
+ for (Entry<Integer, FastFixedSet<Integer>> entry : mapExtPostdominators.entrySet()) {
+ res.put(entry.getKey(), entry.getValue().toPlainSet());
+ }
+
+ return res;
+ }
+
+
+ private void filterOnDominance(DominatorTreeExceptionFilter filter) {
+
+ DominatorEngine engine = filter.getDomEngine();
+
+ for (Integer head : new HashSet<Integer>(mapExtPostdominators.keySet())) {
+
+ FastFixedSet<Integer> setPostdoms = mapExtPostdominators.get(head);
+
+ LinkedList<Statement> stack = new LinkedList<Statement>();
+ LinkedList<FastFixedSet<Integer>> stackPath = new LinkedList<FastFixedSet<Integer>>();
+
+ stack.add(statement.getStats().getWithKey(head));
+ stackPath.add(factory.spawnEmptySet());
+
+ Set<Statement> setVisited = new HashSet<Statement>();
+
+ while (!stack.isEmpty()) {
+
+ Statement stat = stack.removeFirst();
+ FastFixedSet<Integer> path = stackPath.removeFirst();
+
+ if (setPostdoms.contains(stat.id)) {
+ path.add(stat.id);
+ }
+
+ if (path.contains(setPostdoms)) {
+ continue;
+ }
+
+ setVisited.add(stat);
+
+ int domflag = 0;
+
+ for (Iterator<Integer> it = setPostdoms.iterator(); it.hasNext(); ) {
+ Integer post = it.next();
+
+ if (!path.contains(post)) {
+ if (domflag == 0) {
+ domflag = engine.isDominator(stat.id, head) ? 2 : 1;
+ }
+
+ if (domflag == 1) { // not a dominator
+ it.remove();
+ }
+ }
+ }
+
+ for (StatEdge edge : stat.getSuccessorEdges(StatEdge.TYPE_REGULAR)) {
+ if (!setVisited.contains(edge.getDestination())) {
+ stack.add(edge.getDestination());
+ stackPath.add(path.getCopy());
+ }
+ }
+ }
+
+ if (setPostdoms.isEmpty()) {
+ mapExtPostdominators.remove(head);
+ }
+ }
+ }
+
+
+ private void filterOnExceptionRanges(DominatorTreeExceptionFilter filter) {
+
+
+ for (Integer head : new HashSet<Integer>(mapExtPostdominators.keySet())) {
+
+ FastFixedSet<Integer> set = mapExtPostdominators.get(head);
+ for (Iterator<Integer> it = set.iterator(); it.hasNext(); ) {
+ if (!filter.acceptStatementPair(head, it.next())) {
+ it.remove();
+ }
+ }
+ if (set.isEmpty()) {
+ mapExtPostdominators.remove(head);
+ }
+ }
+ }
+
+
+ private void removeErroneousNodes() {
+
+ mapSupportPoints = new HashMap<Integer, FastFixedSet<Integer>>();
+
+ calcReachabilitySuppPoints(StatEdge.TYPE_REGULAR);
+
+ iterateReachability(new IReachabilityAction() {
+ public boolean action(Statement node, HashMap<Integer, FastFixedSet<Integer>> mapSets) {
+
+ Integer nodeid = node.id;
+
+ FastFixedSet<Integer> setReachability = mapSets.get(nodeid);
+ List<FastFixedSet<Integer>> lstPredSets = new ArrayList<FastFixedSet<Integer>>();
+
+ for (StatEdge prededge : node.getPredecessorEdges(StatEdge.TYPE_REGULAR)) {
+ FastFixedSet<Integer> setPred = mapSets.get(prededge.getSource().id);
+ if (setPred == null) {
+ setPred = mapSupportPoints.get(prededge.getSource().id);
+ }
+
+ // setPred cannot be empty as it is a reachability set
+ lstPredSets.add(setPred);
+ }
+
+ for (Integer id : setReachability.toPlainSet()) {
+
+ FastFixedSet<Integer> setReachabilityCopy = setReachability.getCopy();
+
+ FastFixedSet<Integer> setIntersection = factory.spawnEmptySet();
+ boolean isIntersectionInitialized = false;
+
+ for (FastFixedSet<Integer> predset : lstPredSets) {
+ if (predset.contains(id)) {
+ if (!isIntersectionInitialized) {
+ setIntersection.union(predset);
+ isIntersectionInitialized = true;
+ }
+ else {
+ setIntersection.intersection(predset);
+ }
+ }
+ }
+
+ if (nodeid != id.intValue()) {
+ setIntersection.add(nodeid);
+ }
+ else {
+ setIntersection.remove(nodeid);
+ }
+
+ setReachabilityCopy.complement(setIntersection);
+
+ mapExtPostdominators.get(id).complement(setReachabilityCopy);
+ }
+
+ return false;
+ }
+ }, StatEdge.TYPE_REGULAR);
+
+ // exception handlers cannot be postdominator nodes
+ // TODO: replace with a standard set?
+ FastFixedSet<Integer> setHandlers = factory.spawnEmptySet();
+ boolean handlerfound = false;
+
+ for (Statement stat : statement.getStats()) {
+ if (stat.getPredecessorEdges(Statement.STATEDGE_DIRECT_ALL).isEmpty() &&
+ !stat.getPredecessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty()) { // exception handler
+ setHandlers.add(stat.id);
+ handlerfound = true;
+ }
+ }
+
+ if (handlerfound) {
+ for (FastFixedSet<Integer> set : mapExtPostdominators.values()) {
+ set.complement(setHandlers);
+ }
+ }
+ }
+
+
+ private void calcDefaultReachableSets() {
+
+ int edgetype = StatEdge.TYPE_REGULAR | StatEdge.TYPE_EXCEPTION;
+
+ calcReachabilitySuppPoints(edgetype);
+
+ for (Statement stat : statement.getStats()) {
+ mapExtPostdominators.put(stat.id, factory.spawnEmptySet());
+ }
+
+ iterateReachability(new IReachabilityAction() {
+ public boolean action(Statement node, HashMap<Integer, FastFixedSet<Integer>> mapSets) {
+
+ Integer nodeid = node.id;
+ FastFixedSet<Integer> setReachability = mapSets.get(nodeid);
+
+ for (Integer id : setReachability.toPlainSet()) {
+ mapExtPostdominators.get(id).add(nodeid);
+ }
+
+ return false;
+ }
+ }, edgetype);
+ }
+
+
+ private void calcReachabilitySuppPoints(final int edgetype) {
+
+ iterateReachability(new IReachabilityAction() {
+ public boolean action(Statement node, HashMap<Integer, FastFixedSet<Integer>> mapSets) {
+
+ // consider to be a support point
+ for (StatEdge sucedge : node.getAllSuccessorEdges()) {
+ if ((sucedge.getType() & edgetype) != 0) {
+ if (mapSets.containsKey(sucedge.getDestination().id)) {
+ FastFixedSet<Integer> setReachability = mapSets.get(node.id);
+
+ if (!InterpreterUtil.equalObjects(setReachability, mapSupportPoints.get(node.id))) {
+ mapSupportPoints.put(node.id, setReachability);
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+ }, edgetype);
+ }
+
+ private void iterateReachability(IReachabilityAction action, int edgetype) {
+
+ while (true) {
+
+ boolean iterate = false;
+
+ HashMap<Integer, FastFixedSet<Integer>> mapSets = new HashMap<Integer, FastFixedSet<Integer>>();
+
+ for (Statement stat : lstReversePostOrderList) {
+
+ FastFixedSet<Integer> set = factory.spawnEmptySet();
+ set.add(stat.id);
+
+ for (StatEdge prededge : stat.getAllPredecessorEdges()) {
+ if ((prededge.getType() & edgetype) != 0) {
+ Statement pred = prededge.getSource();
+
+ FastFixedSet<Integer> setPred = mapSets.get(pred.id);
+ if (setPred == null) {
+ setPred = mapSupportPoints.get(pred.id);
+ }
+
+ if (setPred != null) {
+ set.union(setPred);
+ }
+ }
+ }
+
+ mapSets.put(stat.id, set);
+
+ if (action != null) {
+ iterate |= action.action(stat, mapSets);
+ }
+
+ // remove reachability information of fully processed nodes (saves memory)
+ for (StatEdge prededge : stat.getAllPredecessorEdges()) {
+ if ((prededge.getType() & edgetype) != 0) {
+ Statement pred = prededge.getSource();
+
+ if (mapSets.containsKey(pred.id)) {
+ boolean remstat = true;
+ for (StatEdge sucedge : pred.getAllSuccessorEdges()) {
+ if ((sucedge.getType() & edgetype) != 0) {
+ if (!mapSets.containsKey(sucedge.getDestination().id)) {
+ remstat = false;
+ break;
+ }
+ }
+ }
+
+ if (remstat) {
+ mapSets.put(pred.id, null);
+ }
+ }
+ }
+ }
+ }
+
+ if (!iterate) {
+ break;
+ }
+ }
+ }
+
+
+ private interface IReachabilityAction {
+ boolean action(Statement node, HashMap<Integer, FastFixedSet<Integer>> mapSets);
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/GenericDominatorEngine.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/GenericDominatorEngine.java
new file mode 100644
index 000000000000..c6eb35722eb6
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/GenericDominatorEngine.java
@@ -0,0 +1,152 @@
+/*
+ * 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.modules.decompiler.decompose;
+
+import org.jetbrains.java.decompiler.util.VBStyleCollection;
+
+import java.util.List;
+import java.util.Set;
+
+public class GenericDominatorEngine {
+
+ private IGraph graph;
+
+ private VBStyleCollection<IGraphNode, IGraphNode> colOrderedIDoms = new VBStyleCollection<IGraphNode, IGraphNode>();
+
+ private Set<? extends IGraphNode> setRoots;
+
+ public GenericDominatorEngine(IGraph graph) {
+ this.graph = graph;
+ }
+
+ public void initialize() {
+ calcIDoms();
+ }
+
+ private void orderNodes() {
+
+ setRoots = graph.getRoots();
+
+ for (IGraphNode node : graph.getReversePostOrderList()) {
+ colOrderedIDoms.addWithKey(null, node);
+ }
+ }
+
+ private static IGraphNode getCommonIDom(IGraphNode node1, IGraphNode node2, VBStyleCollection<IGraphNode, IGraphNode> orderedIDoms) {
+
+ IGraphNode nodeOld;
+
+ if (node1 == null) {
+ return node2;
+ }
+ else if (node2 == null) {
+ return node1;
+ }
+
+ int index1 = orderedIDoms.getIndexByKey(node1);
+ int index2 = orderedIDoms.getIndexByKey(node2);
+
+ while (index1 != index2) {
+ if (index1 > index2) {
+ nodeOld = node1;
+ node1 = orderedIDoms.getWithKey(node1);
+
+ if (nodeOld == node1) { // no idom - root or merging point
+ return null;
+ }
+
+ index1 = orderedIDoms.getIndexByKey(node1);
+ }
+ else {
+ nodeOld = node2;
+ node2 = orderedIDoms.getWithKey(node2);
+
+ if (nodeOld == node2) { // no idom - root or merging point
+ return null;
+ }
+
+ index2 = orderedIDoms.getIndexByKey(node2);
+ }
+ }
+
+ return node1;
+ }
+
+ private void calcIDoms() {
+
+ orderNodes();
+
+ List<IGraphNode> lstNodes = colOrderedIDoms.getLstKeys();
+
+ while (true) {
+
+ boolean changed = false;
+
+ for (IGraphNode node : lstNodes) {
+
+ IGraphNode idom = null;
+
+ if (!setRoots.contains(node)) {
+ for (IGraphNode pred : node.getPredecessors()) {
+ if (colOrderedIDoms.getWithKey(pred) != null) {
+ idom = getCommonIDom(idom, pred, colOrderedIDoms);
+ if (idom == null) {
+ break; // no idom found: merging point of two trees
+ }
+ }
+ }
+ }
+
+ if (idom == null) {
+ idom = node;
+ }
+
+ IGraphNode oldidom = colOrderedIDoms.putWithKey(idom, node);
+ if (!idom.equals(oldidom)) { // oldidom is null iff the node is touched for the first time
+ changed = true;
+ }
+ }
+
+ if (!changed) {
+ break;
+ }
+ }
+ }
+
+ public VBStyleCollection<IGraphNode, IGraphNode> getOrderedIDoms() {
+ return colOrderedIDoms;
+ }
+
+ public boolean isDominator(IGraphNode node, IGraphNode dom) {
+
+ while (!node.equals(dom)) {
+
+ IGraphNode idom = colOrderedIDoms.getWithKey(node);
+
+ if (idom == node) {
+ return false; // root node or merging point
+ }
+ else if (idom == null) {
+ throw new RuntimeException("Inconsistent idom sequence discovered!");
+ }
+ else {
+ node = idom;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraph.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraph.java
new file mode 100644
index 000000000000..faaf4c6b66cf
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraph.java
@@ -0,0 +1,26 @@
+/*
+ * 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.modules.decompiler.decompose;
+
+import java.util.List;
+import java.util.Set;
+
+public interface IGraph {
+
+ List<? extends IGraphNode> getReversePostOrderList();
+
+ Set<? extends IGraphNode> getRoots();
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraphNode.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraphNode.java
new file mode 100644
index 000000000000..19d59b5acb32
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraphNode.java
@@ -0,0 +1,23 @@
+/*
+ * 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.modules.decompiler.decompose;
+
+import java.util.List;
+
+public interface IGraphNode {
+
+ List<? extends IGraphNode> getPredecessors();
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java
new file mode 100644
index 000000000000..7614807c99a4
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java
@@ -0,0 +1,330 @@
+/*
+ * 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.modules.decompiler.deobfuscator;
+
+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;
+import org.jetbrains.java.decompiler.modules.decompiler.decompose.GenericDominatorEngine;
+import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraph;
+import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraphNode;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+public class ExceptionDeobfuscator {
+
+ private static class Range {
+ private final BasicBlock handler;
+ private final String uniqueStr;
+ private final Set<BasicBlock> protectedRange;
+ private final ExceptionRangeCFG rangeCFG;
+
+ private Range(BasicBlock handler, String uniqueStr, Set<BasicBlock> protectedRange, ExceptionRangeCFG rangeCFG) {
+ this.handler = handler;
+ this.uniqueStr = uniqueStr;
+ this.protectedRange = protectedRange;
+ this.rangeCFG = rangeCFG;
+ }
+ }
+
+ public static void restorePopRanges(ControlFlowGraph graph) {
+
+ List<Range> lstRanges = new ArrayList<Range>();
+
+ // aggregate ranges
+ for (ExceptionRangeCFG range : graph.getExceptions()) {
+ boolean found = false;
+ for (Range arr : lstRanges) {
+ if (arr.handler == range.getHandler() && InterpreterUtil.equalObjects(range.getUniqueExceptionsString(), arr.uniqueStr)) {
+ arr.protectedRange.addAll(range.getProtectedRange());
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ // doesn't matter, which range chosen
+ lstRanges.add(new Range(range.getHandler(), range.getUniqueExceptionsString(), new HashSet<BasicBlock>(range.getProtectedRange()), range));
+ }
+ }
+
+ // process aggregated ranges
+ for (Range range : lstRanges) {
+
+ if (range.uniqueStr != null) {
+
+ BasicBlock handler = range.handler;
+ InstructionSequence seq = handler.getSeq();
+
+ Instruction firstinstr;
+ if (seq.length() > 0) {
+ firstinstr = seq.getInstr(0);
+
+ if (firstinstr.opcode == CodeConstants.opc_pop ||
+ firstinstr.opcode == CodeConstants.opc_astore) {
+ Set<BasicBlock> setrange = new HashSet<BasicBlock>(range.protectedRange);
+
+ for (Range range_super : lstRanges) { // finally or strict superset
+
+ if (range != range_super) {
+
+ Set<BasicBlock> setrange_super = new HashSet<BasicBlock>(range_super.protectedRange);
+
+ if (!setrange.contains(range_super.handler) && !setrange_super.contains(handler)
+ && (range_super.uniqueStr == null || setrange_super.containsAll(setrange))) {
+
+ if (range_super.uniqueStr == null) {
+ setrange_super.retainAll(setrange);
+ }
+ else {
+ setrange_super.removeAll(setrange);
+ }
+
+ if (!setrange_super.isEmpty()) {
+
+ BasicBlock newblock = handler;
+
+ // split the handler
+ if (seq.length() > 1) {
+ newblock = new BasicBlock(++graph.last_id);
+ InstructionSequence newseq = new SimpleInstructionSequence();
+ newseq.addInstruction(firstinstr.clone(), -1);
+
+ newblock.setSeq(newseq);
+ graph.getBlocks().addWithKey(newblock, newblock.id);
+
+
+ List<BasicBlock> lstTemp = new ArrayList<BasicBlock>();
+ lstTemp.addAll(handler.getPreds());
+ lstTemp.addAll(handler.getPredExceptions());
+
+ // replace predecessors
+ for (BasicBlock pred : lstTemp) {
+ pred.replaceSuccessor(handler, newblock);
+ }
+
+ // replace handler
+ for (ExceptionRangeCFG range_ext : graph.getExceptions()) {
+ if (range_ext.getHandler() == handler) {
+ range_ext.setHandler(newblock);
+ }
+ else if (range_ext.getProtectedRange().contains(handler)) {
+ newblock.addSuccessorException(range_ext.getHandler());
+ range_ext.getProtectedRange().add(newblock);
+ }
+ }
+
+ newblock.addSuccessor(handler);
+ if (graph.getFirst() == handler) {
+ graph.setFirst(newblock);
+ }
+
+ // remove the first pop in the handler
+ seq.removeInstruction(0);
+ }
+
+ newblock.addSuccessorException(range_super.handler);
+ range_super.rangeCFG.getProtectedRange().add(newblock);
+
+ handler = range.rangeCFG.getHandler();
+ seq = handler.getSeq();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public static void insertEmptyExceptionHandlerBlocks(ControlFlowGraph graph) {
+
+ Set<BasicBlock> setVisited = new HashSet<BasicBlock>();
+
+ for (ExceptionRangeCFG range : graph.getExceptions()) {
+ BasicBlock handler = range.getHandler();
+
+ if (setVisited.contains(handler)) {
+ continue;
+ }
+ setVisited.add(handler);
+
+ BasicBlock emptyblock = new BasicBlock(++graph.last_id);
+ graph.getBlocks().addWithKey(emptyblock, emptyblock.id);
+
+ List<BasicBlock> lstTemp = new ArrayList<BasicBlock>();
+ // only exception predecessors considered
+ lstTemp.addAll(handler.getPredExceptions());
+
+ // replace predecessors
+ for (BasicBlock pred : lstTemp) {
+ pred.replaceSuccessor(handler, emptyblock);
+ }
+
+ // replace handler
+ for (ExceptionRangeCFG range_ext : graph.getExceptions()) {
+ if (range_ext.getHandler() == handler) {
+ range_ext.setHandler(emptyblock);
+ }
+ else if (range_ext.getProtectedRange().contains(handler)) {
+ emptyblock.addSuccessorException(range_ext.getHandler());
+ range_ext.getProtectedRange().add(emptyblock);
+ }
+ }
+
+ emptyblock.addSuccessor(handler);
+ if (graph.getFirst() == handler) {
+ graph.setFirst(emptyblock);
+ }
+ }
+ }
+
+ public static void removeEmptyRanges(ControlFlowGraph graph) {
+
+ List<ExceptionRangeCFG> lstRanges = graph.getExceptions();
+ for (int i = lstRanges.size() - 1; i >= 0; i--) {
+ ExceptionRangeCFG range = lstRanges.get(i);
+
+ boolean isEmpty = true;
+ for (BasicBlock block : range.getProtectedRange()) {
+ if (!block.getSeq().isEmpty()) {
+ isEmpty = false;
+ break;
+ }
+ }
+
+ if (isEmpty) {
+ for (BasicBlock block : range.getProtectedRange()) {
+ block.removeSuccessorException(range.getHandler());
+ }
+
+ lstRanges.remove(i);
+ }
+ }
+ }
+
+ public static void removeCircularRanges(final ControlFlowGraph graph) {
+
+ GenericDominatorEngine engine = new GenericDominatorEngine(new IGraph() {
+ public List<? extends IGraphNode> getReversePostOrderList() {
+ return graph.getReversePostOrder();
+ }
+
+ public Set<? extends IGraphNode> getRoots() {
+ return new HashSet<IGraphNode>(Arrays.asList(new IGraphNode[]{graph.getFirst()}));
+ }
+ });
+
+ engine.initialize();
+
+ List<ExceptionRangeCFG> lstRanges = graph.getExceptions();
+ for (int i = lstRanges.size() - 1; i >= 0; i--) {
+ ExceptionRangeCFG range = lstRanges.get(i);
+
+ BasicBlock handler = range.getHandler();
+ List<BasicBlock> rangeList = range.getProtectedRange();
+
+ if (rangeList.contains(handler)) { // TODO: better removing strategy
+
+ List<BasicBlock> lstRemBlocks = getReachableBlocksRestricted(range, engine);
+
+ if (lstRemBlocks.size() < rangeList.size() || rangeList.size() == 1) {
+ for (BasicBlock block : lstRemBlocks) {
+ block.removeSuccessorException(handler);
+ rangeList.remove(block);
+ }
+ }
+
+ if (rangeList.isEmpty()) {
+ lstRanges.remove(i);
+ }
+ }
+ }
+ }
+
+ private static List<BasicBlock> getReachableBlocksRestricted(ExceptionRangeCFG range, GenericDominatorEngine engine) {
+
+ List<BasicBlock> lstRes = new ArrayList<BasicBlock>();
+
+ LinkedList<BasicBlock> stack = new LinkedList<BasicBlock>();
+ Set<BasicBlock> setVisited = new HashSet<BasicBlock>();
+
+ BasicBlock handler = range.getHandler();
+ stack.addFirst(handler);
+
+ while (!stack.isEmpty()) {
+ BasicBlock block = stack.removeFirst();
+
+ setVisited.add(block);
+
+ if (range.getProtectedRange().contains(block) && engine.isDominator(block, handler)) {
+ lstRes.add(block);
+
+ List<BasicBlock> lstSuccs = new ArrayList<BasicBlock>(block.getSuccs());
+ lstSuccs.addAll(block.getSuccExceptions());
+
+ for (BasicBlock succ : lstSuccs) {
+ if (!setVisited.contains(succ)) {
+ stack.add(succ);
+ }
+ }
+ }
+ }
+
+ return lstRes;
+ }
+
+
+ public static boolean hasObfuscatedExceptions(ControlFlowGraph graph) {
+
+ Map<BasicBlock, Set<BasicBlock>> mapRanges = new HashMap<BasicBlock, Set<BasicBlock>>();
+ for (ExceptionRangeCFG range : graph.getExceptions()) {
+ Set<BasicBlock> set = mapRanges.get(range.getHandler());
+ if (set == null) {
+ mapRanges.put(range.getHandler(), set = new HashSet<BasicBlock>());
+ }
+ set.addAll(range.getProtectedRange());
+ }
+
+ for (Entry<BasicBlock, Set<BasicBlock>> ent : mapRanges.entrySet()) {
+ Set<BasicBlock> setEntries = new HashSet<BasicBlock>();
+
+ for (BasicBlock block : ent.getValue()) {
+ Set<BasicBlock> setTemp = new HashSet<BasicBlock>(block.getPreds());
+ setTemp.removeAll(ent.getValue());
+
+ if (!setTemp.isEmpty()) {
+ setEntries.add(block);
+ }
+ }
+
+ if (!setEntries.isEmpty()) {
+ if (setEntries.size() > 1 /*|| ent.getValue().contains(first)*/) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/IrreducibleCFGDeobfuscator.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/IrreducibleCFGDeobfuscator.java
new file mode 100644
index 000000000000..31e171e214d5
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/IrreducibleCFGDeobfuscator.java
@@ -0,0 +1,245 @@
+/*
+ * 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.modules.decompiler.deobfuscator;
+
+import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+
+public class IrreducibleCFGDeobfuscator {
+
+
+ public static boolean isStatementIrreducible(Statement statement) {
+
+ class Node {
+ public Integer id;
+ public Set<Node> preds = new HashSet<Node>();
+ public Set<Node> succs = new HashSet<Node>();
+
+ public Node(Integer id) {
+ this.id = id;
+ }
+ }
+
+ HashMap<Integer, Node> mapNodes = new HashMap<Integer, Node>();
+
+ // checking exceptions and creating nodes
+ for (Statement stat : statement.getStats()) {
+ if (!stat.getSuccessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty()) {
+ return false;
+ }
+
+ mapNodes.put(stat.id, new Node(stat.id));
+ }
+
+ // connecting nodes
+ for (Statement stat : statement.getStats()) {
+ Node node = mapNodes.get(stat.id);
+
+ for (Statement succ : stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD)) {
+ Node nodeSucc = mapNodes.get(succ.id);
+
+ node.succs.add(nodeSucc);
+ nodeSucc.preds.add(node);
+ }
+ }
+
+ // transforming and reducing the graph
+ while (true) {
+ int ttype = 0;
+ Node node = null;
+
+ for (Node nd : mapNodes.values()) {
+ if (nd.succs.contains(nd)) { // T1
+ ttype = 1;
+ }
+ else if (nd.preds.size() == 1) { // T2
+ ttype = 2;
+ }
+
+ if (ttype != 0) {
+ node = nd;
+ break;
+ }
+ }
+
+ if (node != null) {
+ if (ttype == 1) {
+ node.succs.remove(node);
+ node.preds.remove(node);
+ }
+ else {
+ Node pred = node.preds.iterator().next();
+
+ pred.succs.addAll(node.succs);
+ pred.succs.remove(node);
+
+ for (Node succ : node.succs) {
+ succ.preds.remove(node);
+ succ.preds.add(pred);
+ }
+
+ mapNodes.remove(node.id);
+ }
+ }
+ else { // no transformation applicable
+ return mapNodes.size() > 1; // reducible iff one node remains
+ }
+ }
+ }
+
+
+ private static Statement getCandidateForSplitting(Statement statement) {
+
+ Statement candidateForSplitting = null;
+ int sizeCandidateForSplitting = Integer.MAX_VALUE;
+ int succsCandidateForSplitting = Integer.MAX_VALUE;
+
+ for (Statement stat : statement.getStats()) {
+
+ Set<Statement> setPreds = stat.getNeighboursSet(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD);
+
+ if (setPreds.size() > 1) {
+ int succCount = stat.getNeighboursSet(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD).size();
+ if (succCount <= succsCandidateForSplitting) {
+ int size = getStatementSize(stat) * (setPreds.size() - 1);
+
+ if (succCount < succsCandidateForSplitting ||
+ size < sizeCandidateForSplitting) {
+ candidateForSplitting = stat;
+ sizeCandidateForSplitting = size;
+ succsCandidateForSplitting = succCount;
+ }
+ }
+ }
+ }
+
+ return candidateForSplitting;
+ }
+
+ public static boolean splitIrreducibleNode(Statement statement) {
+
+ Statement splitnode = getCandidateForSplitting(statement);
+ if (splitnode == null) {
+ return false;
+ }
+
+ StatEdge enteredge = splitnode.getPredecessorEdges(StatEdge.TYPE_REGULAR).iterator().next();
+
+ // copy the smallest statement
+ Statement splitcopy = copyStatement(splitnode, null, new HashMap<Statement, Statement>());
+ initCopiedStatement(splitcopy);
+
+ // insert the copy
+ splitcopy.setParent(statement);
+ statement.getStats().addWithKey(splitcopy, splitcopy.id);
+
+ // switch input edges
+ for (StatEdge prededge : splitnode.getPredecessorEdges(Statement.STATEDGE_DIRECT_ALL)) {
+ if (prededge.getSource() == enteredge.getSource() ||
+ prededge.closure == enteredge.getSource()) {
+ splitnode.removePredecessor(prededge);
+ prededge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, prededge, splitcopy);
+ splitcopy.addPredecessor(prededge);
+ }
+ }
+
+ // connect successors
+ for (StatEdge succ : splitnode.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL)) {
+ splitcopy.addSuccessor(new StatEdge(succ.getType(), splitcopy, succ.getDestination(), succ.closure));
+ }
+
+ return true;
+ }
+
+ private static int getStatementSize(Statement statement) {
+
+ int res = 0;
+
+ if (statement.type == Statement.TYPE_BASICBLOCK) {
+ res = ((BasicBlockStatement)statement).getBlock().getSeq().length();
+ }
+ else {
+ for (Statement stat : statement.getStats()) {
+ res += getStatementSize(stat);
+ }
+ }
+
+ return res;
+ }
+
+ private static Statement copyStatement(Statement from, Statement to, HashMap<Statement, Statement> mapAltToCopies) {
+
+ if (to == null) {
+ // first outer invocation
+ to = from.getSimpleCopy();
+ mapAltToCopies.put(from, to);
+ }
+
+ // copy statements
+ for (Statement st : from.getStats()) {
+ Statement stcopy = st.getSimpleCopy();
+
+ to.getStats().addWithKey(stcopy, stcopy.id);
+ mapAltToCopies.put(st, stcopy);
+ }
+
+ // copy edges
+ for (int i = 0; i < from.getStats().size(); i++) {
+ Statement stold = from.getStats().get(i);
+ Statement stnew = to.getStats().get(i);
+
+ for (StatEdge edgeold : stold.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL)) {
+ // type cannot be TYPE_EXCEPTION (checked in isIrreducibleTriangle)
+ StatEdge edgenew = new StatEdge(edgeold.getType(), stnew,
+ mapAltToCopies.containsKey(edgeold.getDestination())
+ ? mapAltToCopies.get(edgeold.getDestination())
+ : edgeold.getDestination(),
+ mapAltToCopies.containsKey(edgeold.closure)
+ ? mapAltToCopies.get(edgeold.closure)
+ : edgeold.closure);
+
+ stnew.addSuccessor(edgenew);
+ }
+ }
+
+ // recurse statements
+ for (int i = 0; i < from.getStats().size(); i++) {
+ Statement stold = from.getStats().get(i);
+ Statement stnew = to.getStats().get(i);
+
+ copyStatement(stold, stnew, mapAltToCopies);
+ }
+
+ return to;
+ }
+
+ private static void initCopiedStatement(Statement statement) {
+
+ statement.initSimpleCopy();
+ statement.setCopied(true);
+
+ for (Statement st : statement.getStats()) {
+ st.setParent(statement);
+ initCopiedStatement(st);
+ }
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AnnotationExprent.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AnnotationExprent.java
new file mode 100644
index 000000000000..9383d76559a5
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AnnotationExprent.java
@@ -0,0 +1,114 @@
+/*
+ * 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.modules.decompiler.exps;
+
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+
+import java.util.List;
+
+
+public class AnnotationExprent extends Exprent {
+
+ public static final int ANNOTATION_NORMAL = 1;
+ public static final int ANNOTATION_MARKER = 2;
+ public static final int ANNOTATION_SINGLE_ELEMENT = 3;
+
+
+ private String classname;
+
+ private List<String> parnames;
+
+ private List<Exprent> parvalues;
+
+ {
+ this.type = EXPRENT_ANNOTATION;
+ }
+
+ public AnnotationExprent(String classname, List<String> parnames, List<Exprent> parvalues) {
+ this.classname = classname;
+ this.parnames = parnames;
+ this.parvalues = parvalues;
+ }
+
+ public String toJava(int indent) {
+
+ String new_line_separator = DecompilerContext.getNewLineSeparator();
+
+ StringBuilder buffer = new StringBuilder();
+ String indstr = InterpreterUtil.getIndentString(indent);
+
+ buffer.append(indstr);
+ buffer.append("@");
+ buffer.append(DecompilerContext.getImportCollector().getShortName(ExprProcessor.buildJavaClassName(classname)));
+
+ if (!parnames.isEmpty()) {
+ buffer.append("(");
+ if (parnames.size() == 1 && "value".equals(parnames.get(0))) {
+ buffer.append(parvalues.get(0).toJava(indent + 1));
+ }
+ else {
+ String indstr1 = InterpreterUtil.getIndentString(indent + 1);
+
+ for (int i = 0; i < parnames.size(); i++) {
+ buffer.append(new_line_separator).append(indstr1);
+ buffer.append(parnames.get(i));
+ buffer.append(" = ");
+ buffer.append(parvalues.get(i).toJava(indent + 2));
+
+ if (i < parnames.size() - 1) {
+ buffer.append(",");
+ }
+ }
+ buffer.append(new_line_separator).append(indstr);
+ }
+
+ buffer.append(")");
+ }
+
+ return buffer.toString();
+ }
+
+ public int getAnnotationType() {
+
+ if (parnames.isEmpty()) {
+ return ANNOTATION_MARKER;
+ }
+ else {
+ if (parnames.size() == 1 && "value".equals(parnames.get(0))) {
+ return ANNOTATION_SINGLE_ELEMENT;
+ }
+ else {
+ return ANNOTATION_NORMAL;
+ }
+ }
+ }
+
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (o == null || !(o instanceof AnnotationExprent)) return false;
+
+ AnnotationExprent ann = (AnnotationExprent)o;
+ return classname.equals(ann.classname) &&
+ InterpreterUtil.equalLists(parnames, ann.parnames) &&
+ InterpreterUtil.equalLists(parvalues, ann.parvalues);
+ }
+
+ public String getClassname() {
+ return classname;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ArrayExprent.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ArrayExprent.java
new file mode 100644
index 000000000000..e5ead9d7c184
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ArrayExprent.java
@@ -0,0 +1,126 @@
+/*
+ * 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.modules.decompiler.exps;
+
+import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class ArrayExprent extends Exprent {
+
+ private Exprent array;
+
+ private Exprent index;
+
+ private VarType hardtype;
+
+ {
+ this.type = EXPRENT_ARRAY;
+ }
+
+ public ArrayExprent(Exprent array, Exprent index, VarType hardtype) {
+ this.array = array;
+ this.index = index;
+ this.hardtype = hardtype;
+ }
+
+ public Exprent copy() {
+ return new ArrayExprent(array.copy(), index.copy(), hardtype);
+ }
+
+ public VarType getExprType() {
+ VarType exprType = array.getExprType().copy();
+ if (exprType.equals(VarType.VARTYPE_NULL)) {
+ exprType = hardtype.copy();
+ }
+ else {
+ exprType.decArrayDim();
+ }
+
+ return exprType;
+ }
+
+ public int getExprentUse() {
+ return array.getExprentUse() & index.getExprentUse() & Exprent.MULTIPLE_USES;
+ }
+
+ public CheckTypesResult checkExprTypeBounds() {
+ CheckTypesResult result = new CheckTypesResult();
+
+ result.addMinTypeExprent(index, VarType.VARTYPE_BYTECHAR);
+ result.addMaxTypeExprent(index, VarType.VARTYPE_INT);
+
+ return result;
+ }
+
+ public List<Exprent> getAllExprents() {
+ List<Exprent> lst = new ArrayList<Exprent>();
+ lst.add(array);
+ lst.add(index);
+ return lst;
+ }
+
+
+ public String toJava(int indent) {
+ String res = array.toJava(indent);
+
+ if (array.getPrecedence() > getPrecedence()) { // array precedence equals 0
+ res = "(" + res + ")";
+ }
+
+ VarType arrtype = array.getExprType();
+ if (arrtype.arraydim == 0) {
+ VarType objarr = VarType.VARTYPE_OBJECT.copy();
+ objarr.arraydim = 1; // type family does not change
+
+ res = "((" + ExprProcessor.getCastTypeName(objarr) + ")" + res + ")";
+ }
+
+ return res + "[" + index.toJava(indent) + "]";
+ }
+
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (o == null || !(o instanceof ArrayExprent)) return false;
+
+ ArrayExprent arr = (ArrayExprent)o;
+ return InterpreterUtil.equalObjects(array, arr.getArray()) &&
+ InterpreterUtil.equalObjects(index, arr.getIndex());
+ }
+
+ public void replaceExprent(Exprent oldexpr, Exprent newexpr) {
+ if (oldexpr == array) {
+ array = newexpr;
+ }
+
+ if (oldexpr == index) {
+ index = newexpr;
+ }
+ }
+
+ public Exprent getArray() {
+ return array;
+ }
+
+ public Exprent getIndex() {
+ return index;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssertExprent.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssertExprent.java
new file mode 100644
index 000000000000..0983dad0cd9d
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssertExprent.java
@@ -0,0 +1,51 @@
+/*
+ * 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.modules.decompiler.exps;
+
+import java.util.List;
+
+public class AssertExprent extends Exprent {
+
+ private List<Exprent> parameters;
+
+ {
+ this.type = EXPRENT_ASSERT;
+ }
+
+ public AssertExprent(List<Exprent> parameters) {
+ this.parameters = parameters;
+ }
+
+ public String toJava(int indent) {
+
+ StringBuilder buffer = new StringBuilder();
+
+ buffer.append("assert ");
+
+ if (parameters.get(0) == null) {
+ buffer.append("false");
+ }
+ else {
+ buffer.append(parameters.get(0).toJava(indent));
+ }
+ if (parameters.size() > 1) {
+ buffer.append(" : ");
+ buffer.append(parameters.get(1).toJava(indent));
+ }
+
+ return buffer.toString();
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssignmentExprent.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssignmentExprent.java
new file mode 100644
index 000000000000..8ff9fe5a448a
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssignmentExprent.java
@@ -0,0 +1,204 @@
+/*
+ * 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.modules.decompiler.exps;
+
+import org.jetbrains.java.decompiler.code.CodeConstants;
+import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult;
+import org.jetbrains.java.decompiler.struct.StructField;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class AssignmentExprent extends Exprent {
+
+ public static final int CONDITION_NONE = -1;
+
+ private static final String[] funceq = new String[]{
+ " += ", // FUNCTION_ADD
+ " -= ", // FUNCTION_SUB
+ " *= ", // FUNCTION_MUL
+ " /= ", // FUNCTION_DIV
+ " &= ", // FUNCTION_AND
+ " |= ", // FUNCTION_OR
+ " ^= ", // FUNCTION_XOR
+ " %= ", // FUNCTION_REM
+ " <<= ", // FUNCTION_SHL
+ " >>= ", // FUNCTION_SHR
+ " >>>= " // FUNCTION_USHR
+ };
+
+
+ private Exprent left;
+
+ private Exprent right;
+
+ private int condtype = CONDITION_NONE;
+
+ {
+ this.type = EXPRENT_ASSIGNMENT;
+ }
+
+
+ public AssignmentExprent(Exprent left, Exprent right) {
+ this.left = left;
+ this.right = right;
+ }
+
+
+ public VarType getExprType() {
+ return left.getExprType();
+ }
+
+
+ public CheckTypesResult checkExprTypeBounds() {
+ CheckTypesResult result = new CheckTypesResult();
+
+ VarType typeleft = left.getExprType();
+ VarType typeright = right.getExprType();
+
+ if (typeleft.type_family > typeright.type_family) {
+ result.addMinTypeExprent(right, VarType.getMinTypeInFamily(typeleft.type_family));
+ }
+ else if (typeleft.type_family < typeright.type_family) {
+ result.addMinTypeExprent(left, typeright);
+ }
+ else {
+ result.addMinTypeExprent(left, VarType.getCommonSupertype(typeleft, typeright));
+ }
+
+ return result;
+ }
+
+ public List<Exprent> getAllExprents() {
+ List<Exprent> lst = new ArrayList<Exprent>();
+ lst.add(left);
+ lst.add(right);
+ return lst;
+ }
+
+ public Exprent copy() {
+ return new AssignmentExprent(left.copy(), right.copy());
+ }
+
+ public int getPrecedence() {
+ return 13;
+ }
+
+ public String toJava(int indent) {
+ VarType leftType = left.getExprType();
+ VarType rightType = right.getExprType();
+
+ boolean fieldInClassInit = false, hiddenField = false;
+ if (left.type == Exprent.EXPRENT_FIELD) { // first assignment to a final field. Field name without "this" in front of it
+ FieldExprent field = (FieldExprent)left;
+ ClassNode node = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE));
+ if (node != null) {
+ StructField fd = node.classStruct.getField(field.getName(), field.getDescriptor().descriptorString);
+ if (fd != null) {
+ if (field.isStatic() && fd.hasModifier(CodeConstants.ACC_FINAL)) {
+ fieldInClassInit = true;
+ }
+ if (node.wrapper.getHiddenMembers().contains(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()))) {
+ hiddenField = true;
+ }
+ }
+ }
+ }
+
+ if (hiddenField) {
+ return "";
+ }
+
+ StringBuilder buffer = new StringBuilder();
+
+ if (fieldInClassInit) {
+ buffer.append(((FieldExprent)left).getName());
+ }
+ else {
+ buffer.append(left.toJava(indent));
+ }
+
+ String res = right.toJava(indent);
+
+ if (condtype == CONDITION_NONE &&
+ !leftType.isSuperset(rightType) &&
+ (rightType.equals(VarType.VARTYPE_OBJECT) || leftType.type != CodeConstants.TYPE_OBJECT)) {
+ if (right.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST)) {
+ res = "(" + res + ")";
+ }
+
+ res = "(" + ExprProcessor.getCastTypeName(leftType) + ")" + res;
+ }
+
+ buffer.append(condtype == CONDITION_NONE ? " = " : funceq[condtype]).append(res);
+
+ return buffer.toString();
+ }
+
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (o == null || !(o instanceof AssignmentExprent)) return false;
+
+ AssignmentExprent as = (AssignmentExprent)o;
+ return InterpreterUtil.equalObjects(left, as.getLeft()) &&
+ InterpreterUtil.equalObjects(right, as.getRight()) &&
+ condtype == as.getCondtype();
+ }
+
+ public void replaceExprent(Exprent oldexpr, Exprent newexpr) {
+ if (oldexpr == left) {
+ left = newexpr;
+ }
+
+ if (oldexpr == right) {
+ right = newexpr;
+ }
+ }
+
+ // *****************************************************************************
+ // getter and setter methods
+ // *****************************************************************************
+
+ public Exprent getLeft() {
+ return left;
+ }
+
+ public void setLeft(Exprent left) {
+ this.left = left;
+ }
+
+ public Exprent getRight() {
+ return right;
+ }
+
+ public void setRight(Exprent right) {
+ this.right = right;
+ }
+
+ public int getCondtype() {
+ return condtype;
+ }
+
+ public void setCondtype(int condtype) {
+ this.condtype = condtype;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java
new file mode 100644
index 000000000000..a45429a85803
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java
@@ -0,0 +1,402 @@
+/*
+ * 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.modules.decompiler.exps;
+
+import org.jetbrains.java.decompiler.code.CodeConstants;
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
+import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
+import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class ConstExprent extends Exprent {
+ private static final HashMap<Integer, String> escapes = new HashMap<Integer, String>();
+
+ static {
+ escapes.put(new Integer(0x8), "\\b"); /* \u0008: backspace BS */
+ escapes.put(new Integer(0x9), "\\t"); /* \u0009: horizontal tab HT */
+ escapes.put(new Integer(0xA), "\\n"); /* \u000a: linefeed LF */
+ escapes.put(new Integer(0xC), "\\f"); /* \u000c: form feed FF */
+ escapes.put(new Integer(0xD), "\\r"); /* \u000d: carriage return CR */
+ escapes.put(new Integer(0x22), "\\\""); /* \u0022: double quote " */
+ escapes.put(new Integer(0x27), "\\\'"); /* \u0027: single quote ' */
+ escapes.put(new Integer(0x5C), "\\\\"); /* \u005c: backslash \ */
+ }
+
+
+ private VarType consttype;
+
+ private Object value;
+
+ private boolean boolPermitted;
+
+ {
+ this.type = EXPRENT_CONST;
+ }
+
+ public ConstExprent(int val, boolean boolPermitted) {
+
+ this.boolPermitted = boolPermitted;
+ if (boolPermitted) {
+ consttype = VarType.VARTYPE_BOOLEAN;
+ if (val != 0 && val != 1) {
+ consttype = consttype.copy();
+ consttype.convinfo |= VarType.FALSEBOOLEAN;
+ }
+ }
+ else {
+ if (0 <= val && val <= 127) {
+ consttype = VarType.VARTYPE_BYTECHAR;
+ }
+ else if (-128 <= val && val <= 127) {
+ consttype = VarType.VARTYPE_BYTE;
+ }
+ else if (0 <= val && val <= 32767) {
+ consttype = VarType.VARTYPE_SHORTCHAR;
+ }
+ else if (-32768 <= val && val <= 32767) {
+ consttype = VarType.VARTYPE_SHORT;
+ }
+ else if (0 <= val && val <= 0xFFFF) {
+ consttype = VarType.VARTYPE_CHAR;
+ }
+ else {
+ consttype = VarType.VARTYPE_INT;
+ }
+ }
+ value = new Integer(val);
+ }
+
+ public ConstExprent(VarType consttype, Object value) {
+ this.consttype = consttype;
+ this.value = value;
+ }
+
+ public Exprent copy() {
+ return new ConstExprent(consttype, value);
+ }
+
+ public VarType getExprType() {
+ return consttype;
+ }
+
+ public int getExprentUse() {
+ return Exprent.MULTIPLE_USES | Exprent.SIDE_EFFECTS_FREE;
+ }
+
+ public List<Exprent> getAllExprents() {
+ return new ArrayList<Exprent>();
+ }
+
+ public String toJava(int indent) {
+ boolean literal = DecompilerContext.getOption(IFernflowerPreferences.LITERALS_AS_IS);
+ boolean ascii = DecompilerContext.getOption(IFernflowerPreferences.ASCII_STRING_CHARACTERS);
+
+ if (consttype.type != CodeConstants.TYPE_NULL && value == null) {
+ return ExprProcessor.getCastTypeName(consttype);
+ }
+ else {
+ switch (consttype.type) {
+ case CodeConstants.TYPE_BOOLEAN:
+ return Boolean.toString(((Integer)value).intValue() != 0);
+ case CodeConstants.TYPE_CHAR:
+ Integer val = (Integer)value;
+ String ret = escapes.get(val);
+ if (ret == null) {
+ char c = (char)val.intValue();
+ if (c >= 32 && c < 127 || !ascii && InterpreterUtil.isPrintableUnicode(c)) {
+ ret = String.valueOf(c);
+ }
+ else {
+ ret = InterpreterUtil.charToUnicodeLiteral(c);
+ }
+ }
+ return "\'" + ret + "\'";
+ case CodeConstants.TYPE_BYTE:
+ case CodeConstants.TYPE_BYTECHAR:
+ case CodeConstants.TYPE_SHORT:
+ case CodeConstants.TYPE_SHORTCHAR:
+ case CodeConstants.TYPE_INT:
+ int ival = ((Integer)value).intValue();
+
+ String intfield;
+ if (literal) {
+ return value.toString();
+ }
+ else if (ival == Integer.MAX_VALUE) {
+ intfield = "MAX_VALUE";
+ }
+ else if (ival == Integer.MIN_VALUE) {
+ intfield = "MIN_VALUE";
+ }
+ else {
+ return value.toString();
+ }
+ return new FieldExprent(intfield, "java/lang/Integer", true, null, FieldDescriptor.INTEGER_DESCRIPTOR).toJava(0);
+ case CodeConstants.TYPE_LONG:
+ long lval = ((Long)value).longValue();
+
+ String longfield;
+ if (literal) {
+ return value.toString() + "L";
+ }
+ else if (lval == Long.MAX_VALUE) {
+ longfield = "MAX_VALUE";
+ }
+ else if (lval == Long.MIN_VALUE) {
+ longfield = "MIN_VALUE";
+ }
+ else {
+ return value.toString() + "L";
+ }
+ return new FieldExprent(longfield, "java/lang/Long", true, null, FieldDescriptor.LONG_DESCRIPTOR).toJava(0);
+ case CodeConstants.TYPE_DOUBLE:
+ double dval = ((Double)value).doubleValue();
+
+ String doublefield;
+ if (literal) {
+ if (Double.isNaN(dval)) {
+ return "0.0D / 0.0";
+ }
+ else if (dval == Double.POSITIVE_INFINITY) {
+ return "1.0D / 0.0";
+ }
+ else if (dval == Double.NEGATIVE_INFINITY) {
+ return "-1.0D / 0.0";
+ }
+ else {
+ return value.toString() + "D";
+ }
+ }
+ else if (Double.isNaN(dval)) {
+ doublefield = "NaN";
+ }
+ else if (dval == Double.POSITIVE_INFINITY) {
+ doublefield = "POSITIVE_INFINITY";
+ }
+ else if (dval == Double.NEGATIVE_INFINITY) {
+ doublefield = "NEGATIVE_INFINITY";
+ }
+ else if (dval == Double.MAX_VALUE) {
+ doublefield = "MAX_VALUE";
+ }
+ else if (dval == Double.MIN_VALUE) {
+ doublefield = "MIN_VALUE";
+ }
+ else {
+ return value.toString() + "D";
+ }
+ return new FieldExprent(doublefield, "java/lang/Double", true, null, FieldDescriptor.DOUBLE_DESCRIPTOR).toJava(0);
+ case CodeConstants.TYPE_FLOAT:
+ float fval = ((Float)value).floatValue();
+
+ String floatfield;
+ if (literal) {
+ if (Double.isNaN(fval)) {
+ return "0.0F / 0.0";
+ }
+ else if (fval == Double.POSITIVE_INFINITY) {
+ return "1.0F / 0.0";
+ }
+ else if (fval == Double.NEGATIVE_INFINITY) {
+ return "-1.0F / 0.0";
+ }
+ else {
+ return value.toString() + "F";
+ }
+ }
+ else if (Float.isNaN(fval)) {
+ floatfield = "NaN";
+ }
+ else if (fval == Float.POSITIVE_INFINITY) {
+ floatfield = "POSITIVE_INFINITY";
+ }
+ else if (fval == Float.NEGATIVE_INFINITY) {
+ floatfield = "NEGATIVE_INFINITY";
+ }
+ else if (fval == Float.MAX_VALUE) {
+ floatfield = "MAX_VALUE";
+ }
+ else if (fval == Float.MIN_VALUE) {
+ floatfield = "MIN_VALUE";
+ }
+ else {
+ return value.toString() + "F";
+ }
+ return new FieldExprent(floatfield, "java/lang/Float", true, null, FieldDescriptor.FLOAT_DESCRIPTOR).toJava(0);
+ case CodeConstants.TYPE_NULL:
+ return "null";
+ case CodeConstants.TYPE_OBJECT:
+ if (consttype.equals(VarType.VARTYPE_STRING)) {
+ return "\"" + convertStringToJava(value.toString(), ascii) + "\"";
+ }
+ else if (consttype.equals(VarType.VARTYPE_CLASS)) {
+ String strval = value.toString();
+
+ VarType classtype;
+ if (strval.startsWith("[")) { // array of simple type
+ classtype = new VarType(strval, false);
+ }
+ else { // class
+ classtype = new VarType(strval, true);
+ }
+
+ return ExprProcessor.getCastTypeName(classtype) + ".class";
+ }
+ }
+ }
+
+ throw new RuntimeException("invalid constant type");
+ }
+
+ private static String convertStringToJava(String value, boolean ascii) {
+ char[] arr = value.toCharArray();
+ StringBuilder buffer = new StringBuilder(arr.length);
+
+ for (char c : arr) {
+ switch (c) {
+ case '\\': // u005c: backslash \
+ buffer.append("\\\\");
+ break;
+ case 0x8: // "\\\\b"); // u0008: backspace BS
+ buffer.append("\\b");
+ break;
+ case 0x9: //"\\\\t"); // u0009: horizontal tab HT
+ buffer.append("\\t");
+ break;
+ case 0xA: //"\\\\n"); // u000a: linefeed LF
+ buffer.append("\\n");
+ break;
+ case 0xC: //"\\\\f"); // u000c: form feed FF
+ buffer.append("\\f");
+ break;
+ case 0xD: //"\\\\r"); // u000d: carriage return CR
+ buffer.append("\\r");
+ break;
+ case 0x22: //"\\\\\""); // u0022: double quote "
+ buffer.append("\\\"");
+ break;
+ case 0x27: //"\\\\'"); // u0027: single quote '
+ buffer.append("\\\'");
+ break;
+ default:
+ if (c >= 32 && c < 127 || !ascii && InterpreterUtil.isPrintableUnicode(c)) {
+ buffer.append(c);
+ }
+ else {
+ buffer.append(InterpreterUtil.charToUnicodeLiteral(c));
+ }
+ }
+ }
+
+ return buffer.toString();
+ }
+
+
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (o == null || !(o instanceof ConstExprent)) return false;
+
+ ConstExprent cn = (ConstExprent)o;
+ return InterpreterUtil.equalObjects(consttype, cn.getConsttype()) &&
+ InterpreterUtil.equalObjects(value, cn.getValue());
+ }
+
+ public boolean hasBooleanValue() {
+
+ switch (consttype.type) {
+ case CodeConstants.TYPE_BOOLEAN:
+ case CodeConstants.TYPE_CHAR:
+ case CodeConstants.TYPE_BYTE:
+ case CodeConstants.TYPE_BYTECHAR:
+ case CodeConstants.TYPE_SHORT:
+ case CodeConstants.TYPE_SHORTCHAR:
+ case CodeConstants.TYPE_INT:
+ Integer ival = (Integer)value;
+ return ival.intValue() == 0 ||
+ (DecompilerContext.getOption(IFernflowerPreferences.BOOLEAN_TRUE_ONE) && ival.intValue() == 1);
+ }
+
+ return false;
+ }
+
+ public boolean hasValueOne() {
+
+ switch (consttype.type) {
+ case CodeConstants.TYPE_BOOLEAN:
+ case CodeConstants.TYPE_CHAR:
+ case CodeConstants.TYPE_BYTE:
+ case CodeConstants.TYPE_BYTECHAR:
+ case CodeConstants.TYPE_SHORT:
+ case CodeConstants.TYPE_SHORTCHAR:
+ case CodeConstants.TYPE_INT:
+ return ((Integer)value).intValue() == 1;
+ case CodeConstants.TYPE_LONG:
+ return ((Long)value).intValue() == 1;
+ case CodeConstants.TYPE_DOUBLE:
+ return ((Double)value).intValue() == 1;
+ case CodeConstants.TYPE_FLOAT:
+ return ((Float)value).intValue() == 1;
+ }
+
+ return false;
+ }
+
+ public static ConstExprent getZeroConstant(int type) {
+
+ switch (type) {
+ case CodeConstants.TYPE_INT:
+ return new ConstExprent(VarType.VARTYPE_INT, new Integer(0));
+ case CodeConstants.TYPE_LONG:
+ return new ConstExprent(VarType.VARTYPE_LONG, new Long(0));
+ case CodeConstants.TYPE_DOUBLE:
+ return new ConstExprent(VarType.VARTYPE_DOUBLE, new Double(0));
+ case CodeConstants.TYPE_FLOAT:
+ return new ConstExprent(VarType.VARTYPE_FLOAT, new Float(0));
+ }
+
+ throw new RuntimeException("Invalid argument!");
+ }
+
+ public VarType getConsttype() {
+ return consttype;
+ }
+
+ public void setConsttype(VarType consttype) {
+ this.consttype = consttype;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public int getIntValue() {
+ return ((Integer)value).intValue();
+ }
+
+ public boolean isBoolPermitted() {
+ return boolPermitted;
+ }
+
+ public void setBoolPermitted(boolean boolPermitted) {
+ this.boolPermitted = boolPermitted;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java
new file mode 100644
index 000000000000..7a5013432952
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java
@@ -0,0 +1,151 @@
+/*
+ * 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.modules.decompiler.exps;
+
+import org.jetbrains.java.decompiler.code.CodeConstants;
+import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.main.rels.MethodWrapper;
+import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult;
+import org.jetbrains.java.decompiler.struct.attr.StructExceptionsAttribute;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class ExitExprent extends Exprent {
+
+ public static final int EXIT_RETURN = 0;
+ public static final int EXIT_THROW = 1;
+
+ // return or throw statement
+ private int exittype;
+
+ private Exprent value;
+
+ private VarType rettype;
+
+ {
+ this.type = EXPRENT_EXIT;
+ }
+
+ public ExitExprent(int exittype, Exprent value, VarType rettype) {
+ this.exittype = exittype;
+ this.value = value;
+ this.rettype = rettype;
+ }
+
+ public Exprent copy() {
+ return new ExitExprent(exittype, value == null ? null : value.copy(), rettype);
+ }
+
+ public CheckTypesResult checkExprTypeBounds() {
+ CheckTypesResult result = new CheckTypesResult();
+
+ if (exittype == EXIT_RETURN && rettype.type != CodeConstants.TYPE_VOID) {
+ result.addMinTypeExprent(value, VarType.getMinTypeInFamily(rettype.type_family));
+ result.addMaxTypeExprent(value, rettype);
+ }
+
+ return result;
+ }
+
+ public List<Exprent> getAllExprents() {
+ List<Exprent> lst = new ArrayList<Exprent>();
+ if (value != null) {
+ lst.add(value);
+ }
+ return lst;
+ }
+
+ public String toJava(int indent) {
+ if (exittype == EXIT_RETURN) {
+ StringBuilder buffer = new StringBuilder();
+
+ if (rettype.type != CodeConstants.TYPE_VOID) {
+ buffer.append(" ");
+ ExprProcessor.getCastedExprent(value, rettype, buffer, indent, false);
+ }
+
+ return "return" + buffer.toString();
+ }
+ else {
+
+ MethodWrapper meth = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER);
+ ClassNode node = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE));
+
+ if (meth != null && node != null) {
+ StructExceptionsAttribute attr = (StructExceptionsAttribute)meth.methodStruct.getAttributes().getWithKey("Exceptions");
+
+ if (attr != null) {
+ String classname = null;
+
+ for (int i = 0; i < attr.getThrowsExceptions().size(); i++) {
+ String excclassname = attr.getExcClassname(i, node.classStruct.getPool());
+ if ("java/lang/Throwable".equals(excclassname)) {
+ classname = excclassname;
+ break;
+ }
+ else if ("java/lang/Exception".equals(excclassname)) {
+ classname = excclassname;
+ }
+ }
+
+ if (classname != null) {
+ VarType exctype = new VarType(classname, true);
+
+ StringBuilder buffer = new StringBuilder();
+ ExprProcessor.getCastedExprent(value, exctype, buffer, indent, false);
+
+ return "throw " + buffer.toString();
+ }
+ }
+ }
+
+ return "throw " + value.toJava(indent);
+ }
+ }
+
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (o == null || !(o instanceof ExitExprent)) return false;
+
+ ExitExprent et = (ExitExprent)o;
+ return exittype == et.getExittype() &&
+ InterpreterUtil.equalObjects(value, et.getValue());
+ }
+
+ public void replaceExprent(Exprent oldexpr, Exprent newexpr) {
+ if (oldexpr == value) {
+ value = newexpr;
+ }
+ }
+
+ public int getExittype() {
+ return exittype;
+ }
+
+ public Exprent getValue() {
+ return value;
+ }
+
+ public VarType getRettype() {
+ return rettype;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java
new file mode 100644
index 000000000000..99712d574ab7
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java
@@ -0,0 +1,133 @@
+/*
+ * 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.modules.decompiler.exps;
+
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+
+public class Exprent {
+
+ public static final int MULTIPLE_USES = 1;
+ public static final int SIDE_EFFECTS_FREE = 2;
+ public static final int BOTH_FLAGS = 3;
+
+
+ public static final int EXPRENT_ARRAY = 1;
+ public static final int EXPRENT_ASSIGNMENT = 2;
+ public static final int EXPRENT_CONST = 3;
+ public static final int EXPRENT_EXIT = 4;
+ public static final int EXPRENT_FIELD = 5;
+ public static final int EXPRENT_FUNCTION = 6;
+ public static final int EXPRENT_IF = 7;
+ public static final int EXPRENT_INVOCATION = 8;
+ public static final int EXPRENT_MONITOR = 9;
+ public static final int EXPRENT_NEW = 10;
+ public static final int EXPRENT_SWITCH = 11;
+ public static final int EXPRENT_VAR = 12;
+ public static final int EXPRENT_ANNOTATION = 13;
+ public static final int EXPRENT_ASSERT = 14;
+
+ public int type;
+
+ public int id;
+
+ {
+ // set exprent id
+ id = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.EXPRENT_COUNTER);
+ }
+
+ public int getPrecedence() {
+ return 0; // the highest precedence
+ }
+
+ public VarType getExprType() {
+ return VarType.VARTYPE_VOID;
+ }
+
+ public int getExprentUse() {
+ return 0;
+ }
+
+ public CheckTypesResult checkExprTypeBounds() {
+ return new CheckTypesResult();
+ }
+
+ public boolean containsExprent(Exprent exprent) {
+
+ List<Exprent> listTemp = new ArrayList<Exprent>(getAllExprents(true));
+ listTemp.add(this);
+
+ for (Exprent lstexpr : listTemp) {
+ if (lstexpr.equals(exprent)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public List<Exprent> getAllExprents(boolean recursive) {
+ List<Exprent> lst = getAllExprents();
+
+ if (recursive) {
+ for (int i = lst.size() - 1; i >= 0; i--) {
+ lst.addAll(lst.get(i).getAllExprents(true));
+ }
+ }
+
+ return lst;
+ }
+
+ public Set<VarVersionPaar> getAllVariables() {
+
+ HashSet<VarVersionPaar> set = new HashSet<VarVersionPaar>();
+
+ List<Exprent> lstAllExprents = getAllExprents(true);
+ lstAllExprents.add(this);
+
+ for (Exprent expr : lstAllExprents) {
+ if (expr.type == EXPRENT_VAR) {
+ set.add(new VarVersionPaar((VarExprent)expr));
+ }
+ }
+
+ return set;
+ }
+
+ public List<Exprent> getAllExprents() {
+ throw new RuntimeException("not implemented");
+ }
+
+ public Exprent copy() {
+ throw new RuntimeException("not implemented");
+ }
+
+ public String toJava(int indent) {
+ throw new RuntimeException("not implemented");
+ }
+
+ public void replaceExprent(Exprent oldexpr, Exprent newexpr) {
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java
new file mode 100644
index 000000000000..aa180b038798
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java
@@ -0,0 +1,200 @@
+/*
+ * 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.modules.decompiler.exps;
+
+import org.jetbrains.java.decompiler.code.CodeConstants;
+import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.main.rels.MethodWrapper;
+import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar;
+import org.jetbrains.java.decompiler.struct.StructClass;
+import org.jetbrains.java.decompiler.struct.consts.LinkConstant;
+import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class FieldExprent extends Exprent {
+
+ private String name;
+
+ private String classname;
+
+ private boolean isStatic;
+
+ private Exprent instance;
+
+ private FieldDescriptor descriptor;
+
+ {
+ this.type = EXPRENT_FIELD;
+ }
+
+ public FieldExprent(LinkConstant cn, Exprent instance) {
+
+ this.instance = instance;
+
+ if (instance == null) {
+ isStatic = true;
+ }
+
+ classname = cn.classname;
+ name = cn.elementname;
+ descriptor = FieldDescriptor.parseDescriptor(cn.descriptor);
+ }
+
+ public FieldExprent(String name, String classname, boolean isStatic, Exprent instance, FieldDescriptor descriptor) {
+ this.name = name;
+ this.classname = classname;
+ this.isStatic = isStatic;
+ this.instance = instance;
+ this.descriptor = descriptor;
+ }
+
+ public VarType getExprType() {
+ return descriptor.type;
+ }
+
+ public int getExprentUse() {
+ if (instance == null) {
+ return Exprent.MULTIPLE_USES;
+ }
+ else {
+ return instance.getExprentUse() & Exprent.MULTIPLE_USES;
+ }
+ }
+
+ public List<Exprent> getAllExprents() {
+ List<Exprent> lst = new ArrayList<Exprent>();
+ if (instance != null) {
+ lst.add(instance);
+ }
+ return lst;
+ }
+
+ public Exprent copy() {
+ return new FieldExprent(name, classname, isStatic, instance == null ? null : instance.copy(), descriptor);
+ }
+
+ public String toJava(int indent) {
+ StringBuilder buf = new StringBuilder();
+
+
+ if (isStatic) {
+ ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE);
+ if (node == null || !classname.equals(node.classStruct.qualifiedName)) {
+ buf.append(DecompilerContext.getImportCollector().getShortName(ExprProcessor.buildJavaClassName(classname)));
+ buf.append(".");
+ }
+ }
+ else {
+
+ String super_qualifier = null;
+
+ if (instance != null && instance.type == Exprent.EXPRENT_VAR) {
+ VarExprent instvar = (VarExprent)instance;
+ VarVersionPaar varpaar = new VarVersionPaar(instvar);
+
+ MethodWrapper current_meth = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER);
+
+ if (current_meth != null) { // FIXME: remove
+ String this_classname = current_meth.varproc.getThisvars().get(varpaar);
+
+ if (this_classname != null) {
+ if (!classname.equals(this_classname)) { // TODO: direct comparison to the super class?
+ super_qualifier = this_classname;
+ }
+ }
+ }
+ }
+
+ if (super_qualifier != null) {
+ StructClass current_class = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE)).classStruct;
+
+ if (!super_qualifier.equals(current_class.qualifiedName)) {
+ buf.append(DecompilerContext.getImportCollector().getShortName(ExprProcessor.buildJavaClassName(super_qualifier)));
+ buf.append(".");
+ }
+ buf.append("super");
+ }
+ else {
+ StringBuilder buff = new StringBuilder();
+ boolean casted = ExprProcessor.getCastedExprent(instance, new VarType(CodeConstants.TYPE_OBJECT, 0, classname), buff, indent, true);
+ String res = buff.toString();
+
+ if (casted || instance.getPrecedence() > getPrecedence()) {
+ res = "(" + res + ")";
+ }
+
+ buf.append(res);
+ }
+
+ if (buf.toString().equals(
+ VarExprent.VAR_NAMELESS_ENCLOSURE)) { // FIXME: workaround for field access of an anonymous enclosing class. Find a better way.
+ buf.setLength(0);
+ }
+ else {
+ buf.append(".");
+ }
+ }
+
+ buf.append(name);
+
+ return buf.toString();
+ }
+
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (o == null || !(o instanceof FieldExprent)) return false;
+
+ FieldExprent ft = (FieldExprent)o;
+ return InterpreterUtil.equalObjects(name, ft.getName()) &&
+ InterpreterUtil.equalObjects(classname, ft.getClassname()) &&
+ isStatic == ft.isStatic() &&
+ InterpreterUtil.equalObjects(instance, ft.getInstance()) &&
+ InterpreterUtil.equalObjects(descriptor, ft.getDescriptor());
+ }
+
+ public void replaceExprent(Exprent oldexpr, Exprent newexpr) {
+ if (oldexpr == instance) {
+ instance = newexpr;
+ }
+ }
+
+ public String getClassname() {
+ return classname;
+ }
+
+ public FieldDescriptor getDescriptor() {
+ return descriptor;
+ }
+
+ public Exprent getInstance() {
+ return instance;
+ }
+
+ public boolean isStatic() {
+ return isStatic;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java
new file mode 100644
index 000000000000..4fa2ab385cee
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java
@@ -0,0 +1,615 @@
+/*
+ * 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.modules.decompiler.exps;
+
+import org.jetbrains.java.decompiler.code.CodeConstants;
+import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+import org.jetbrains.java.decompiler.util.ListStack;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+
+public class FunctionExprent extends Exprent {
+
+ public static final int FUNCTION_ADD = 0;
+ public static final int FUNCTION_SUB = 1;
+ public static final int FUNCTION_MUL = 2;
+ public static final int FUNCTION_DIV = 3;
+
+ public static final int FUNCTION_AND = 4;
+ public static final int FUNCTION_OR = 5;
+ public static final int FUNCTION_XOR = 6;
+
+ public static final int FUNCTION_REM = 7;
+
+ public static final int FUNCTION_SHL = 8;
+ public static final int FUNCTION_SHR = 9;
+ public static final int FUNCTION_USHR = 10;
+
+ public static final int FUNCTION_BITNOT = 11;
+ public static final int FUNCTION_BOOLNOT = 12;
+ public static final int FUNCTION_NEG = 13;
+
+ public final static int FUNCTION_I2L = 14;
+ public final static int FUNCTION_I2F = 15;
+ public final static int FUNCTION_I2D = 16;
+ public final static int FUNCTION_L2I = 17;
+ public final static int FUNCTION_L2F = 18;
+ public final static int FUNCTION_L2D = 19;
+ public final static int FUNCTION_F2I = 20;
+ public final static int FUNCTION_F2L = 21;
+ public final static int FUNCTION_F2D = 22;
+ public final static int FUNCTION_D2I = 23;
+ public final static int FUNCTION_D2L = 24;
+ public final static int FUNCTION_D2F = 25;
+ public final static int FUNCTION_I2B = 26;
+ public final static int FUNCTION_I2C = 27;
+ public final static int FUNCTION_I2S = 28;
+
+ public final static int FUNCTION_CAST = 29;
+ public final static int FUNCTION_INSTANCEOF = 30;
+
+ public final static int FUNCTION_ARRAYLENGTH = 31;
+
+ public final static int FUNCTION_IMM = 32;
+ public final static int FUNCTION_MMI = 33;
+
+ public final static int FUNCTION_IPP = 34;
+ public final static int FUNCTION_PPI = 35;
+
+ public final static int FUNCTION_IIF = 36;
+
+ public final static int FUNCTION_LCMP = 37;
+ public final static int FUNCTION_FCMPL = 38;
+ public final static int FUNCTION_FCMPG = 39;
+ public final static int FUNCTION_DCMPL = 40;
+ public final static int FUNCTION_DCMPG = 41;
+
+ public static final int FUNCTION_EQ = 42;
+ public static final int FUNCTION_NE = 43;
+ public static final int FUNCTION_LT = 44;
+ public static final int FUNCTION_GE = 45;
+ public static final int FUNCTION_GT = 46;
+ public static final int FUNCTION_LE = 47;
+
+ public static final int FUNCTION_CADD = 48;
+ public static final int FUNCTION_COR = 49;
+
+ public static final int FUNCTION_STRCONCAT = 50;
+
+ private static final VarType[] types = new VarType[]{
+ VarType.VARTYPE_LONG,
+ VarType.VARTYPE_FLOAT,
+ VarType.VARTYPE_DOUBLE,
+ VarType.VARTYPE_INT,
+ VarType.VARTYPE_FLOAT,
+ VarType.VARTYPE_DOUBLE,
+ VarType.VARTYPE_INT,
+ VarType.VARTYPE_LONG,
+ VarType.VARTYPE_DOUBLE,
+ VarType.VARTYPE_INT,
+ VarType.VARTYPE_LONG,
+ VarType.VARTYPE_FLOAT,
+ VarType.VARTYPE_BYTE,
+ VarType.VARTYPE_CHAR,
+ VarType.VARTYPE_SHORT
+ };
+
+ private static final String[] operators = new String[]{
+ " + ",
+ " - ",
+ " * ",
+ " / ",
+ " & ",
+ " | ",
+ " ^ ",
+ " % ",
+ " << ",
+ " >> ",
+ " >>> ",
+ " == ",
+ " != ",
+ " < ",
+ " >= ",
+ " > ",
+ " <= ",
+ " && ",
+ " || ",
+ " + "
+ };
+
+ private static final int[] precedence = new int[]{
+ 3, // FUNCTION_ADD
+ 3, // FUNCTION_SUB
+ 2, // FUNCTION_MUL
+ 2, // FUNCTION_DIV
+ 7, // FUNCTION_AND
+ 9, // FUNCTION_OR
+ 8, // FUNCTION_XOR
+ 2, // FUNCTION_REM
+ 4, // FUNCTION_SHL
+ 4, // FUNCTION_SHR
+ 4, // FUNCTION_USHR
+ 1, // FUNCTION_BITNOT
+ 1, // FUNCTION_BOOLNOT
+ 1, // FUNCTION_NEG
+ 1, // FUNCTION_I2L
+ 1, // FUNCTION_I2F
+ 1, // FUNCTION_I2D
+ 1, // FUNCTION_L2I
+ 1, // FUNCTION_L2F
+ 1, // FUNCTION_L2D
+ 1, // FUNCTION_F2I
+ 1, // FUNCTION_F2L
+ 1, // FUNCTION_F2D
+ 1, // FUNCTION_D2I
+ 1, // FUNCTION_D2L
+ 1, // FUNCTION_D2F
+ 1, // FUNCTION_I2B
+ 1, // FUNCTION_I2C
+ 1, // FUNCTION_I2S
+ 1, // FUNCTION_CAST
+ 6, // FUNCTION_INSTANCEOF
+ 0, // FUNCTION_ARRAYLENGTH
+ 1, // FUNCTION_IMM
+ 1, // FUNCTION_MMI
+ 1, // FUNCTION_IPP
+ 1, // FUNCTION_PPI
+ 12, // FUNCTION_IFF
+ -1, // FUNCTION_LCMP
+ -1, // FUNCTION_FCMPL
+ -1, // FUNCTION_FCMPG
+ -1, // FUNCTION_DCMPL
+ -1, // FUNCTION_DCMPG
+ 6, // FUNCTION_EQ = 41;
+ 6, // FUNCTION_NE = 42;
+ 5, // FUNCTION_LT = 43;
+ 5, // FUNCTION_GE = 44;
+ 5, // FUNCTION_GT = 45;
+ 5, // FUNCTION_LE = 46;
+ 10, // FUNCTION_CADD = 47;
+ 11, // FUNCTION_COR = 48;
+ 3 // FUNCTION_STRCONCAT = 49;
+ };
+
+ private static final HashSet<Integer> associativity =
+ new HashSet<Integer>(Arrays.asList(new Integer[]{FUNCTION_ADD, FUNCTION_MUL, FUNCTION_AND,
+ FUNCTION_OR, FUNCTION_XOR, FUNCTION_CADD, FUNCTION_COR, FUNCTION_STRCONCAT}));
+
+ private int functype;
+
+ private VarType implicitType;
+
+ private List<Exprent> lstOperands = new ArrayList<Exprent>();
+
+ {
+ this.type = EXPRENT_FUNCTION;
+ }
+
+ public FunctionExprent(int functype, ListStack<Exprent> stack) {
+ this.functype = functype;
+ if (functype >= FUNCTION_BITNOT && functype <= FUNCTION_PPI && functype != FUNCTION_CAST
+ && functype != FUNCTION_INSTANCEOF) {
+ lstOperands.add(stack.pop());
+ }
+ else if (functype == FUNCTION_IIF) {
+ throw new RuntimeException("no direct instantiation possible");
+ }
+ else {
+ Exprent expr = stack.pop();
+ lstOperands.add(stack.pop());
+ lstOperands.add(expr);
+ }
+ }
+
+ public FunctionExprent(int functype, List<Exprent> operands) {
+ this.functype = functype;
+ this.lstOperands = operands;
+ }
+
+ public VarType getExprType() {
+ VarType exprType = null;
+
+ if (functype <= FUNCTION_NEG || functype == FUNCTION_IPP || functype == FUNCTION_PPI
+ || functype == FUNCTION_IMM || functype == FUNCTION_MMI) {
+
+ VarType type1 = lstOperands.get(0).getExprType();
+ VarType type2 = null;
+ if (lstOperands.size() > 1) {
+ type2 = lstOperands.get(1).getExprType();
+ }
+
+ switch (functype) {
+ case FUNCTION_IMM:
+ case FUNCTION_MMI:
+ case FUNCTION_IPP:
+ case FUNCTION_PPI:
+ exprType = implicitType;
+ break;
+ case FUNCTION_BOOLNOT:
+ exprType = VarType.VARTYPE_BOOLEAN;
+ break;
+ case FUNCTION_SHL:
+ case FUNCTION_SHR:
+ case FUNCTION_USHR:
+ case FUNCTION_BITNOT:
+ case FUNCTION_NEG:
+ exprType = getMaxVarType(new VarType[]{type1});
+ break;
+ case FUNCTION_ADD:
+ case FUNCTION_SUB:
+ case FUNCTION_MUL:
+ case FUNCTION_DIV:
+ case FUNCTION_REM:
+ exprType = getMaxVarType(new VarType[]{type1, type2});
+ break;
+ case FUNCTION_AND:
+ case FUNCTION_OR:
+ case FUNCTION_XOR:
+ if (type1.type == CodeConstants.TYPE_BOOLEAN & type2.type == CodeConstants.TYPE_BOOLEAN) {
+ exprType = VarType.VARTYPE_BOOLEAN;
+ }
+ else {
+ exprType = getMaxVarType(new VarType[]{type1, type2});
+ }
+ }
+ }
+ else if (functype == FUNCTION_CAST) {
+ exprType = lstOperands.get(1).getExprType();
+ }
+ else if (functype == FUNCTION_IIF) {
+ Exprent param1 = lstOperands.get(1);
+ Exprent param2 = lstOperands.get(2);
+ VarType supertype = VarType.getCommonSupertype(param1.getExprType(), param2.getExprType());
+
+ if (param1.type == Exprent.EXPRENT_CONST && param2.type == Exprent.EXPRENT_CONST &&
+ supertype.type != CodeConstants.TYPE_BOOLEAN && VarType.VARTYPE_INT.isSuperset(supertype)) {
+ exprType = VarType.VARTYPE_INT;
+ }
+ else {
+ exprType = supertype;
+ }
+ }
+ else if (functype == FUNCTION_STRCONCAT) {
+ exprType = VarType.VARTYPE_STRING;
+ }
+ else if (functype >= FUNCTION_EQ || functype == FUNCTION_INSTANCEOF) {
+ exprType = VarType.VARTYPE_BOOLEAN;
+ }
+ else if (functype >= FUNCTION_ARRAYLENGTH) {
+ exprType = VarType.VARTYPE_INT;
+ }
+ else {
+ exprType = types[functype - FUNCTION_I2L];
+ }
+
+ return exprType;
+ }
+
+ public int getExprentUse() {
+
+ if (functype >= FUNCTION_IMM && functype <= FUNCTION_PPI) {
+ return 0;
+ }
+ else {
+ int ret = Exprent.MULTIPLE_USES | Exprent.SIDE_EFFECTS_FREE;
+ for (Exprent expr : lstOperands) {
+ ret &= expr.getExprentUse();
+ }
+ return ret;
+ }
+ }
+
+ public CheckTypesResult checkExprTypeBounds() {
+ CheckTypesResult result = new CheckTypesResult();
+
+ Exprent param1 = lstOperands.get(0);
+ VarType type1 = param1.getExprType();
+ Exprent param2 = null;
+ VarType type2 = null;
+
+ if (lstOperands.size() > 1) {
+ param2 = lstOperands.get(1);
+ type2 = param2.getExprType();
+ }
+
+ switch (functype) {
+ case FUNCTION_IIF:
+ VarType supertype = getExprType();
+ if (supertype == null) {
+ supertype = getExprType();
+ }
+ result.addMinTypeExprent(param1, VarType.VARTYPE_BOOLEAN);
+ result.addMinTypeExprent(param2, VarType.getMinTypeInFamily(supertype.type_family));
+ result.addMinTypeExprent(lstOperands.get(2), VarType.getMinTypeInFamily(supertype.type_family));
+ break;
+ case FUNCTION_I2L:
+ case FUNCTION_I2F:
+ case FUNCTION_I2D:
+ case FUNCTION_I2B:
+ case FUNCTION_I2C:
+ case FUNCTION_I2S:
+ result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR);
+ result.addMaxTypeExprent(param1, VarType.VARTYPE_INT);
+ break;
+ case FUNCTION_IMM:
+ case FUNCTION_IPP:
+ case FUNCTION_MMI:
+ case FUNCTION_PPI:
+ result.addMinTypeExprent(param1, implicitType);
+ result.addMaxTypeExprent(param1, implicitType);
+ break;
+ case FUNCTION_ADD:
+ case FUNCTION_SUB:
+ case FUNCTION_MUL:
+ case FUNCTION_DIV:
+ case FUNCTION_REM:
+ case FUNCTION_SHL:
+ case FUNCTION_SHR:
+ case FUNCTION_USHR:
+ case FUNCTION_LT:
+ case FUNCTION_GE:
+ case FUNCTION_GT:
+ case FUNCTION_LE:
+ result.addMinTypeExprent(param2, VarType.VARTYPE_BYTECHAR);
+ case FUNCTION_BITNOT:
+ // case FUNCTION_BOOLNOT:
+ case FUNCTION_NEG:
+ result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR);
+ break;
+ case FUNCTION_AND:
+ case FUNCTION_OR:
+ case FUNCTION_XOR:
+ case FUNCTION_EQ:
+ case FUNCTION_NE: {
+ if (type1.type == CodeConstants.TYPE_BOOLEAN) {
+
+ if (type2.isStrictSuperset(type1)) {
+
+ result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR);
+ }
+ else { // both are booleans
+
+ boolean param1_false_boolean =
+ type1.isFalseBoolean() || (param1.type == Exprent.EXPRENT_CONST && !((ConstExprent)param1).hasBooleanValue());
+ boolean param2_false_boolean =
+ type1.isFalseBoolean() || (param2.type == Exprent.EXPRENT_CONST && !((ConstExprent)param2).hasBooleanValue());
+
+ if (param1_false_boolean || param2_false_boolean) {
+ result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR);
+ result.addMinTypeExprent(param2, VarType.VARTYPE_BYTECHAR);
+ }
+ }
+ }
+ else if (type2.type == CodeConstants.TYPE_BOOLEAN) {
+
+ if (type1.isStrictSuperset(type2)) {
+ result.addMinTypeExprent(param2, VarType.VARTYPE_BYTECHAR);
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ public List<Exprent> getAllExprents() {
+ List<Exprent> lst = new ArrayList<Exprent>();
+ lst.addAll(lstOperands);
+ return lst;
+ }
+
+ public Exprent copy() {
+ List<Exprent> lst = new ArrayList<Exprent>();
+ for (Exprent expr : lstOperands) {
+ lst.add(expr.copy());
+ }
+ FunctionExprent func = new FunctionExprent(functype, lst);
+ func.setImplicitType(implicitType);
+
+ return func;
+ }
+
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (o == null || !(o instanceof FunctionExprent)) return false;
+
+ FunctionExprent fe = (FunctionExprent)o;
+ return functype == fe.getFunctype() &&
+ InterpreterUtil.equalLists(lstOperands, fe.getLstOperands()); // TODO: order of operands insignificant
+ }
+
+ public void replaceExprent(Exprent oldexpr, Exprent newexpr) {
+ for (int i = 0; i < lstOperands.size(); i++) {
+ if (oldexpr == lstOperands.get(i)) {
+ lstOperands.set(i, newexpr);
+ }
+ }
+ }
+
+ public String toJava(int indent) {
+
+ if (functype <= FUNCTION_USHR) {
+ return wrapOperandString(lstOperands.get(0), false, indent) + operators[functype] +
+ wrapOperandString(lstOperands.get(1), true, indent);
+ }
+
+ if (functype >= FUNCTION_EQ) {
+ return wrapOperandString(lstOperands.get(0), false, indent) + operators[functype - FUNCTION_EQ + 11] +
+ wrapOperandString(lstOperands.get(1), true, indent);
+ }
+
+ switch (functype) {
+ case FUNCTION_BITNOT:
+ return "~" + wrapOperandString(lstOperands.get(0), true, indent);
+ case FUNCTION_BOOLNOT:
+ return "!" + wrapOperandString(lstOperands.get(0), true, indent);
+ case FUNCTION_NEG:
+ return "-" + wrapOperandString(lstOperands.get(0), true, indent);
+ case FUNCTION_CAST:
+ return "(" + lstOperands.get(1).toJava(indent) + ")" + wrapOperandString(lstOperands.get(0), true, indent);
+ case FUNCTION_ARRAYLENGTH:
+ Exprent arr = lstOperands.get(0);
+
+ String res = wrapOperandString(arr, false, indent);
+ if (arr.getExprType().arraydim == 0) {
+ VarType objarr = VarType.VARTYPE_OBJECT.copy();
+ objarr.arraydim = 1; // type family does not change
+
+ res = "((" + ExprProcessor.getCastTypeName(objarr) + ")" + res + ")";
+ }
+ return res + ".length";
+ case FUNCTION_IIF:
+ return wrapOperandString(lstOperands.get(0), true, indent) + "?" + wrapOperandString(lstOperands.get(1), true, indent) + ":" +
+ wrapOperandString(lstOperands.get(2), true, indent);
+ case FUNCTION_IPP:
+ return wrapOperandString(lstOperands.get(0), true, indent) + "++";
+ case FUNCTION_PPI:
+ return "++" + wrapOperandString(lstOperands.get(0), true, indent);
+ case FUNCTION_IMM:
+ return wrapOperandString(lstOperands.get(0), true, indent) + "--";
+ case FUNCTION_MMI:
+ return "--" + wrapOperandString(lstOperands.get(0), true, indent);
+ case FUNCTION_INSTANCEOF:
+ return wrapOperandString(lstOperands.get(0), true, indent) + " instanceof " + wrapOperandString(lstOperands.get(1), true, indent);
+ case FUNCTION_LCMP: // shouldn't appear in the final code
+ return "__lcmp__(" +
+ wrapOperandString(lstOperands.get(0), true, indent) +
+ "," +
+ wrapOperandString(lstOperands.get(1), true, indent) +
+ ")";
+ case FUNCTION_FCMPL: // shouldn't appear in the final code
+ return "__fcmpl__(" +
+ wrapOperandString(lstOperands.get(0), true, indent) +
+ "," +
+ wrapOperandString(lstOperands.get(1), true, indent) +
+ ")";
+ case FUNCTION_FCMPG: // shouldn't appear in the final code
+ return "__fcmpg__(" +
+ wrapOperandString(lstOperands.get(0), true, indent) +
+ "," +
+ wrapOperandString(lstOperands.get(1), true, indent) +
+ ")";
+ case FUNCTION_DCMPL: // shouldn't appear in the final code
+ return "__dcmpl__(" +
+ wrapOperandString(lstOperands.get(0), true, indent) +
+ "," +
+ wrapOperandString(lstOperands.get(1), true, indent) +
+ ")";
+ case FUNCTION_DCMPG: // shouldn't appear in the final code
+ return "__dcmpg__(" +
+ wrapOperandString(lstOperands.get(0), true, indent) +
+ "," +
+ wrapOperandString(lstOperands.get(1), true, indent) +
+ ")";
+ }
+
+ if (functype <= FUNCTION_I2S) {
+ return "(" + ExprProcessor.getTypeName(types[functype - FUNCTION_I2L]) + ")" + wrapOperandString(lstOperands.get(0), true, indent);
+ }
+
+ // return "<unknown function>";
+ throw new RuntimeException("invalid function");
+ }
+
+ public int getPrecedence() {
+ return getPrecedence(functype);
+ }
+
+ public static int getPrecedence(int func) {
+ return precedence[func];
+ }
+
+ public VarType getSimpleCastType() {
+ return types[functype - FUNCTION_I2L];
+ }
+
+ private String wrapOperandString(Exprent expr, boolean eq, int indent) {
+
+ int myprec = getPrecedence();
+ int exprprec = expr.getPrecedence();
+
+ boolean parentheses = exprprec > myprec;
+ if (!parentheses && eq) {
+ parentheses = (exprprec == myprec);
+ if (parentheses) {
+ if (expr.type == Exprent.EXPRENT_FUNCTION &&
+ ((FunctionExprent)expr).getFunctype() == functype) {
+ parentheses = !associativity.contains(functype);
+ }
+ }
+ }
+
+ String res = expr.toJava(indent);
+
+ if (parentheses) {
+ res = "(" + res + ")";
+ }
+
+ return res;
+ }
+
+ private static VarType getMaxVarType(VarType[] arr) {
+
+ int[] types = new int[]{CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_LONG};
+ VarType[] vartypes = new VarType[]{VarType.VARTYPE_DOUBLE, VarType.VARTYPE_FLOAT, VarType.VARTYPE_LONG};
+
+ for (int i = 0; i < types.length; i++) {
+ for (int j = 0; j < arr.length; j++) {
+ if (arr[j].type == types[i]) {
+ return vartypes[i];
+ }
+ }
+ }
+
+ return VarType.VARTYPE_INT;
+ }
+
+ // *****************************************************************************
+ // getter and setter methods
+ // *****************************************************************************
+
+ public int getFunctype() {
+ return functype;
+ }
+
+ public void setFunctype(int functype) {
+ this.functype = functype;
+ }
+
+ public List<Exprent> getLstOperands() {
+ return lstOperands;
+ }
+
+ public void setLstOperands(List<Exprent> lstOperands) {
+ this.lstOperands = lstOperands;
+ }
+
+ public VarType getImplicitType() {
+ return implicitType;
+ }
+
+ public void setImplicitType(VarType implicitType) {
+ this.implicitType = implicitType;
+ }
+}
+
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/IfExprent.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/IfExprent.java
new file mode 100644
index 000000000000..66e63c0e7fc6
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/IfExprent.java
@@ -0,0 +1,145 @@
+/*
+ * 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.modules.decompiler.exps;
+
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+import org.jetbrains.java.decompiler.util.ListStack;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+
+public class IfExprent extends Exprent {
+
+ public static final int IF_EQ = 0;
+ public static final int IF_NE = 1;
+ public static final int IF_LT = 2;
+ public static final int IF_GE = 3;
+ public static final int IF_GT = 4;
+ public static final int IF_LE = 5;
+
+ public static final int IF_NULL = 6;
+ public static final int IF_NONNULL = 7;
+
+ public static final int IF_ICMPEQ = 8;
+ public static final int IF_ICMPNE = 9;
+ public static final int IF_ICMPLT = 10;
+ public static final int IF_ICMPGE = 11;
+ public static final int IF_ICMPGT = 12;
+ public static final int IF_ICMPLE = 13;
+ public static final int IF_ACMPEQ = 14;
+ public static final int IF_ACMPNE = 15;
+
+ public static final int IF_CAND = 16;
+ public static final int IF_COR = 17;
+
+ public static final int IF_NOT = 18;
+ public static final int IF_VALUE = 19;
+
+ private static final int[] functypes = new int[]{
+ FunctionExprent.FUNCTION_EQ,
+ FunctionExprent.FUNCTION_NE,
+ FunctionExprent.FUNCTION_LT,
+ FunctionExprent.FUNCTION_GE,
+ FunctionExprent.FUNCTION_GT,
+ FunctionExprent.FUNCTION_LE,
+ FunctionExprent.FUNCTION_EQ,
+ FunctionExprent.FUNCTION_NE,
+ FunctionExprent.FUNCTION_EQ,
+ FunctionExprent.FUNCTION_NE,
+ FunctionExprent.FUNCTION_LT,
+ FunctionExprent.FUNCTION_GE,
+ FunctionExprent.FUNCTION_GT,
+ FunctionExprent.FUNCTION_LE,
+ FunctionExprent.FUNCTION_EQ,
+ FunctionExprent.FUNCTION_NE,
+ FunctionExprent.FUNCTION_CADD,
+ FunctionExprent.FUNCTION_COR,
+ FunctionExprent.FUNCTION_BOOLNOT,
+ -1
+ };
+
+ private Exprent condition;
+
+ {
+ this.type = EXPRENT_IF;
+ }
+
+ public IfExprent(int iftype, ListStack<Exprent> stack) {
+
+ if (iftype <= IF_LE) {
+ stack.push(new ConstExprent(0, true));
+ }
+ else if (iftype <= IF_NONNULL) {
+ stack.push(new ConstExprent(VarType.VARTYPE_NULL, null));
+ }
+
+ if (iftype == IF_VALUE) {
+ condition = stack.pop();
+ }
+ else {
+ condition = new FunctionExprent(functypes[iftype], stack);
+ }
+ }
+
+ private IfExprent(Exprent condition) {
+ this.condition = condition;
+ }
+
+ public Exprent copy() {
+ return new IfExprent(condition.copy());
+ }
+
+ public List<Exprent> getAllExprents() {
+ List<Exprent> lst = new ArrayList<Exprent>();
+ lst.add(condition);
+ return lst;
+ }
+
+ public String toJava(int indent) {
+ return "if(" + condition.toJava(indent) + ")";
+ }
+
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (o == null || !(o instanceof IfExprent)) return false;
+
+ IfExprent ie = (IfExprent)o;
+ return InterpreterUtil.equalObjects(condition, ie.getCondition());
+ }
+
+ public void replaceExprent(Exprent oldexpr, Exprent newexpr) {
+ if (oldexpr == condition) {
+ condition = newexpr;
+ }
+ }
+
+ public IfExprent negateIf() {
+ condition = new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT,
+ Arrays.asList(new Exprent[]{condition}));
+ return this;
+ }
+
+ public Exprent getCondition() {
+ return condition;
+ }
+
+ public void setCondition(Exprent condition) {
+ this.condition = condition;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java
new file mode 100644
index 000000000000..04758dc0492d
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java
@@ -0,0 +1,515 @@
+/*
+ * 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.modules.decompiler.exps;
+
+import org.jetbrains.java.decompiler.code.CodeConstants;
+import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
+import org.jetbrains.java.decompiler.main.rels.MethodWrapper;
+import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar;
+import org.jetbrains.java.decompiler.struct.StructClass;
+import org.jetbrains.java.decompiler.struct.StructMethod;
+import org.jetbrains.java.decompiler.struct.consts.LinkConstant;
+import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+import org.jetbrains.java.decompiler.util.ListStack;
+
+import java.util.*;
+
+
+public class InvocationExprent extends Exprent {
+
+ public static final int INVOKE_SPECIAL = 1;
+ public static final int INVOKE_VIRTUAL = 2;
+ public static final int INVOKE_STATIC = 3;
+ public static final int INVOKE_INTERFACE = 4;
+ public static final int INVOKE_DYNAMIC = 5;
+
+ public static final int TYP_GENERAL = 1;
+ public static final int TYP_INIT = 2;
+ public static final int TYP_CLINIT = 3;
+
+ public static final int CONSTRUCTOR_NOT = 0;
+ public static final int CONSTRUCTOR_THIS = 1;
+ public static final int CONSTRUCTOR_SUPER = 2;
+
+ private String name;
+
+ private String classname;
+
+ private boolean isStatic;
+
+ private int functype = TYP_GENERAL;
+
+ private Exprent instance;
+
+ private MethodDescriptor descriptor;
+
+ private String stringDescriptor;
+
+ private String invoke_dynamic_classsuffix;
+
+ private int invocationTyp = INVOKE_VIRTUAL;
+
+ private List<Exprent> lstParameters = new ArrayList<Exprent>();
+
+ {
+ this.type = EXPRENT_INVOCATION;
+ }
+
+ public InvocationExprent() {
+ }
+
+ public InvocationExprent(int opcode, LinkConstant cn, ListStack<Exprent> stack, int dynamic_invokation_type) {
+
+ name = cn.elementname;
+ classname = cn.classname;
+
+ switch (opcode) {
+ case CodeConstants.opc_invokestatic:
+ invocationTyp = INVOKE_STATIC;
+ break;
+ case CodeConstants.opc_invokespecial:
+ invocationTyp = INVOKE_SPECIAL;
+ break;
+ case CodeConstants.opc_invokevirtual:
+ invocationTyp = INVOKE_VIRTUAL;
+ break;
+ case CodeConstants.opc_invokeinterface:
+ invocationTyp = INVOKE_INTERFACE;
+ break;
+ case CodeConstants.opc_invokedynamic:
+ invocationTyp = INVOKE_DYNAMIC;
+
+ classname = "java/lang/Class"; // dummy class name
+ invoke_dynamic_classsuffix = "##Lambda_" + cn.index1 + "_" + cn.index2;
+ }
+
+ if ("<init>".equals(name)) {
+ functype = TYP_INIT;
+ }
+ else if ("<clinit>".equals(name)) {
+ functype = TYP_CLINIT;
+ }
+
+ stringDescriptor = cn.descriptor;
+ descriptor = MethodDescriptor.parseDescriptor(cn.descriptor);
+
+ for (int i = 0; i < descriptor.params.length; i++) {
+ lstParameters.add(0, stack.pop());
+ }
+
+ if (opcode == CodeConstants.opc_invokedynamic) {
+ if (dynamic_invokation_type == CodeConstants.CONSTANT_MethodHandle_REF_invokeStatic) {
+ isStatic = true;
+ }
+ else {
+ instance = lstParameters
+ .get(0); // FIXME: remove the first parameter completely from the list. It's the object type for a virtual lambda method.
+ }
+ }
+ else if (opcode == CodeConstants.opc_invokestatic) {
+ isStatic = true;
+ }
+ else {
+ instance = stack.pop();
+ }
+ }
+
+ private InvocationExprent(InvocationExprent expr) {
+ name = expr.getName();
+ classname = expr.getClassname();
+ isStatic = expr.isStatic();
+ functype = expr.getFunctype();
+ instance = expr.getInstance();
+ if (instance != null) {
+ instance = instance.copy();
+ }
+ invocationTyp = expr.getInvocationTyp();
+ stringDescriptor = expr.getStringDescriptor();
+ descriptor = expr.getDescriptor();
+ lstParameters = new ArrayList<Exprent>(expr.getLstParameters());
+ for (int i = 0; i < lstParameters.size(); i++) {
+ lstParameters.set(i, lstParameters.get(i).copy());
+ }
+ }
+
+
+ public VarType getExprType() {
+ return descriptor.ret;
+ }
+
+ public CheckTypesResult checkExprTypeBounds() {
+ CheckTypesResult result = new CheckTypesResult();
+
+ for (int i = 0; i < lstParameters.size(); i++) {
+ Exprent parameter = lstParameters.get(i);
+
+ VarType leftType = descriptor.params[i];
+
+ result.addMinTypeExprent(parameter, VarType.getMinTypeInFamily(leftType.type_family));
+ result.addMaxTypeExprent(parameter, leftType);
+ }
+
+ return result;
+ }
+
+ public List<Exprent> getAllExprents() {
+ List<Exprent> lst = new ArrayList<Exprent>();
+ if (instance != null) {
+ lst.add(instance);
+ }
+ lst.addAll(lstParameters);
+ return lst;
+ }
+
+
+ public Exprent copy() {
+ return new InvocationExprent(this);
+ }
+
+ public String toJava(int indent) {
+ StringBuilder buf = new StringBuilder("");
+
+ String super_qualifier = null;
+ boolean isInstanceThis = false;
+
+ if (invocationTyp == INVOKE_DYNAMIC) {
+ // ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE);
+ //
+ // if(node != null) {
+ // ClassNode lambda_node = DecompilerContext.getClassprocessor().getMapRootClasses().get(node.classStruct.qualifiedName + invoke_dynamic_classsuffix);
+ // if(lambda_node != null) {
+ //
+ // String typename = ExprProcessor.getCastTypeName(lambda_node.anonimousClassType);
+ //
+ // StringWriter strwriter = new StringWriter();
+ // BufferedWriter bufstrwriter = new BufferedWriter(strwriter);
+ //
+ // ClassWriter clwriter = new ClassWriter();
+ //
+ // try {
+ // bufstrwriter.write("new " + typename + "() {");
+ // bufstrwriter.newLine();
+ //
+ //
+ //
+ // bufstrwriter.flush();
+ // } catch(IOException ex) {
+ // throw new RuntimeException(ex);
+ // }
+ //
+ // buf.append(strwriter.toString());
+ //
+ // }
+ // }
+
+ }
+ else if (isStatic) {
+
+ ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE);
+ if (node == null || !classname.equals(node.classStruct.qualifiedName)) {
+ buf.append(DecompilerContext.getImportCollector().getShortName(ExprProcessor.buildJavaClassName(classname)));
+ }
+ }
+ else {
+
+ if (instance != null && instance.type == Exprent.EXPRENT_VAR) {
+ VarExprent instvar = (VarExprent)instance;
+ VarVersionPaar varpaar = new VarVersionPaar(instvar);
+
+ VarProcessor vproc = instvar.getProcessor();
+ if (vproc == null) {
+ MethodWrapper current_meth = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER);
+ if (current_meth != null) {
+ vproc = current_meth.varproc;
+ }
+ }
+
+ String this_classname = null;
+ if (vproc != null) {
+ this_classname = vproc.getThisvars().get(varpaar);
+ }
+
+ if (this_classname != null) {
+ isInstanceThis = true;
+
+ if (invocationTyp == INVOKE_SPECIAL) {
+ if (!classname.equals(this_classname)) { // TODO: direct comparison to the super class?
+ super_qualifier = this_classname;
+ }
+ }
+ }
+ }
+
+ if (functype == TYP_GENERAL) {
+ if (super_qualifier != null) {
+ StructClass current_class = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE)).classStruct;
+
+ if (!super_qualifier.equals(current_class.qualifiedName)) {
+ buf.append(DecompilerContext.getImportCollector().getShortName(ExprProcessor.buildJavaClassName(super_qualifier)));
+ buf.append(".");
+ }
+ buf.append("super");
+ }
+ else {
+ String res = instance.toJava(indent);
+
+ VarType rightType = instance.getExprType();
+ VarType leftType = new VarType(CodeConstants.TYPE_OBJECT, 0, classname);
+
+ if (rightType.equals(VarType.VARTYPE_OBJECT) && !leftType.equals(rightType)) {
+ buf.append("((").append(ExprProcessor.getCastTypeName(leftType)).append(")");
+
+ if (instance.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST)) {
+ res = "(" + res + ")";
+ }
+ buf.append(res).append(")");
+ }
+ else if (instance.getPrecedence() > getPrecedence()) {
+ buf.append("(").append(res).append(")");
+ }
+ else {
+ buf.append(res);
+ }
+ }
+ }
+ }
+
+ switch (functype) {
+ case TYP_GENERAL:
+ if (VarExprent.VAR_NAMELESS_ENCLOSURE.equals(buf.toString())) {
+ buf = new StringBuilder("");
+ }
+
+ if (buf.length() > 0) {
+ buf.append(".");
+ }
+
+ buf.append(name);
+ if (invocationTyp == INVOKE_DYNAMIC) {
+ buf.append("<invokedynamic>");
+ }
+ buf.append("(");
+
+ break;
+ case TYP_CLINIT:
+ throw new RuntimeException("Explicite invocation of <clinit>");
+ case TYP_INIT:
+ if (super_qualifier != null) {
+ buf.append("super(");
+ }
+ else if (isInstanceThis) {
+ buf.append("this(");
+ }
+ else {
+ buf.append(instance.toJava(indent));
+ buf.append(".<init>(");
+ // throw new RuntimeException("Unrecognized invocation of <init>"); // FIXME: activate
+ }
+ }
+
+ List<VarVersionPaar> sigFields = null;
+ boolean isEnum = false;
+ if (functype == TYP_INIT) {
+ ClassNode newNode = DecompilerContext.getClassProcessor().getMapRootClasses().get(classname);
+
+ if (newNode != null) { // own class
+ if (newNode.wrapper != null) {
+ sigFields = newNode.wrapper.getMethodWrapper("<init>", stringDescriptor).signatureFields;
+ }
+ else {
+ if (newNode.type == ClassNode.CLASS_MEMBER && (newNode.access & CodeConstants.ACC_STATIC) == 0) { // non-static member class
+ sigFields = new ArrayList<VarVersionPaar>(Collections.nCopies(lstParameters.size(), (VarVersionPaar)null));
+ sigFields.set(0, new VarVersionPaar(-1, 0));
+ }
+ }
+ isEnum = newNode.classStruct.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM);
+ }
+ }
+
+ Set<Integer> setAmbiguousParameters = getAmbiguousParameters();
+
+ boolean firstpar = true;
+ int start = isEnum ? 2 : 0;
+ for (int i = start; i < lstParameters.size(); i++) {
+ if (sigFields == null || sigFields.get(i) == null) {
+ if (!firstpar) {
+ buf.append(", ");
+ }
+
+ StringBuilder buff = new StringBuilder();
+ ExprProcessor.getCastedExprent(lstParameters.get(i), descriptor.params[i], buff, indent, true, setAmbiguousParameters.contains(i));
+
+ buf.append(buff);
+ firstpar = false;
+ }
+ }
+ buf.append(")");
+
+ return buf.toString();
+ }
+
+ private Set<Integer> getAmbiguousParameters() {
+
+ Set<Integer> ret = new HashSet<Integer>();
+
+ StructClass cstr = DecompilerContext.getStructContext().getClass(classname);
+ if (cstr != null) {
+ List<MethodDescriptor> lstMethods = new ArrayList<MethodDescriptor>();
+ for (StructMethod meth : cstr.getMethods()) {
+ if (name.equals(meth.getName())) {
+ MethodDescriptor md = MethodDescriptor.parseDescriptor(meth.getDescriptor());
+ if (md.params.length == descriptor.params.length) {
+ boolean equals = true;
+ for (int i = 0; i < md.params.length; i++) {
+ if (md.params[i].type_family != descriptor.params[i].type_family) {
+ equals = false;
+ break;
+ }
+ }
+
+ if (equals) {
+ lstMethods.add(md);
+ }
+ }
+ }
+ }
+
+ if (lstMethods.size() > 1) {
+ for (int i = 0; i < descriptor.params.length; i++) {
+ VarType partype = descriptor.params[i];
+
+ for (MethodDescriptor md : lstMethods) {
+ if (!partype.equals(md.params[i])) {
+ ret.add(i);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (o == null || !(o instanceof InvocationExprent)) return false;
+
+ InvocationExprent it = (InvocationExprent)o;
+ return InterpreterUtil.equalObjects(name, it.getName()) &&
+ InterpreterUtil.equalObjects(classname, it.getClassname()) &&
+ isStatic == it.isStatic() &&
+ InterpreterUtil.equalObjects(instance, it.getInstance()) &&
+ InterpreterUtil.equalObjects(descriptor, it.getDescriptor()) &&
+ functype == it.getFunctype() &&
+ InterpreterUtil.equalLists(lstParameters, it.getLstParameters());
+ }
+
+ public void replaceExprent(Exprent oldexpr, Exprent newexpr) {
+ if (oldexpr == instance) {
+ instance = newexpr;
+ }
+
+ for (int i = 0; i < lstParameters.size(); i++) {
+ if (oldexpr == lstParameters.get(i)) {
+ lstParameters.set(i, newexpr);
+ }
+ }
+ }
+
+ public List<Exprent> getLstParameters() {
+ return lstParameters;
+ }
+
+ public void setLstParameters(List<Exprent> lstParameters) {
+ this.lstParameters = lstParameters;
+ }
+
+ public MethodDescriptor getDescriptor() {
+ return descriptor;
+ }
+
+ public void setDescriptor(MethodDescriptor descriptor) {
+ this.descriptor = descriptor;
+ }
+
+ public String getClassname() {
+ return classname;
+ }
+
+ public void setClassname(String classname) {
+ this.classname = classname;
+ }
+
+ public int getFunctype() {
+ return functype;
+ }
+
+ public void setFunctype(int functype) {
+ this.functype = functype;
+ }
+
+ public Exprent getInstance() {
+ return instance;
+ }
+
+ public void setInstance(Exprent instance) {
+ this.instance = instance;
+ }
+
+ public boolean isStatic() {
+ return isStatic;
+ }
+
+ public void setStatic(boolean isStatic) {
+ this.isStatic = isStatic;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getStringDescriptor() {
+ return stringDescriptor;
+ }
+
+ public void setStringDescriptor(String stringDescriptor) {
+ this.stringDescriptor = stringDescriptor;
+ }
+
+ public int getInvocationTyp() {
+ return invocationTyp;
+ }
+
+ public void setInvocationTyp(int invocationTyp) {
+ this.invocationTyp = invocationTyp;
+ }
+
+ public String getInvokeDynamicClassSuffix() {
+ return invoke_dynamic_classsuffix;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/MonitorExprent.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/MonitorExprent.java
new file mode 100644
index 000000000000..9a1945e85ac7
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/MonitorExprent.java
@@ -0,0 +1,83 @@
+/*
+ * 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.modules.decompiler.exps;
+
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class MonitorExprent extends Exprent {
+
+ public static final int MONITOR_ENTER = 0;
+ public static final int MONITOR_EXIT = 1;
+
+ private int montype;
+
+ private Exprent value;
+
+ {
+ this.type = EXPRENT_MONITOR;
+ }
+
+ public MonitorExprent(int montype, Exprent value) {
+ this.montype = montype;
+ this.value = value;
+ }
+
+ public Exprent copy() {
+ return new MonitorExprent(montype, value.copy());
+ }
+
+ public List<Exprent> getAllExprents() {
+ List<Exprent> lst = new ArrayList<Exprent>();
+ lst.add(value);
+ return lst;
+ }
+
+ public String toJava(int indent) {
+ if (montype == MONITOR_ENTER) {
+ return "synchronized(" + value.toJava(indent) + ")";
+ }
+ else {
+ return "";
+ }
+ }
+
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (o == null || !(o instanceof MonitorExprent)) return false;
+
+ MonitorExprent me = (MonitorExprent)o;
+ return montype == me.getMontype() &&
+ InterpreterUtil.equalObjects(value, me.getValue());
+ }
+
+ public void replaceExprent(Exprent oldexpr, Exprent newexpr) {
+ if (oldexpr == value) {
+ value = newexpr;
+ }
+ }
+
+ public int getMontype() {
+ return montype;
+ }
+
+ public Exprent getValue() {
+ return value;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java
new file mode 100644
index 000000000000..416b831283f1
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java
@@ -0,0 +1,504 @@
+/*
+ * 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.modules.decompiler.exps;
+
+import org.jetbrains.java.decompiler.code.CodeConstants;
+import org.jetbrains.java.decompiler.main.ClassWriter;
+import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
+import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar;
+import org.jetbrains.java.decompiler.struct.StructClass;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+import org.jetbrains.java.decompiler.util.ListStack;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class NewExprent extends Exprent {
+
+ private InvocationExprent constructor;
+
+ private VarType newtype;
+
+ private List<Exprent> lstDims = new ArrayList<Exprent>();
+
+ private List<Exprent> lstArrayElements = new ArrayList<Exprent>();
+
+ private boolean directArrayInit;
+
+ private boolean anonymous;
+
+ private boolean lambda;
+
+ private boolean enumconst;
+
+ {
+ this.type = EXPRENT_NEW;
+ }
+
+ public NewExprent(VarType newtype, ListStack<Exprent> stack, int arraydim) {
+ this.newtype = newtype;
+ for (int i = 0; i < arraydim; i++) {
+ lstDims.add(0, stack.pop());
+ }
+
+ setAnonymous();
+ }
+
+ public NewExprent(VarType newtype, List<Exprent> lstDims) {
+ this.newtype = newtype;
+ this.lstDims = lstDims;
+
+ setAnonymous();
+ }
+
+ private void setAnonymous() {
+
+ anonymous = false;
+ lambda = false;
+
+ if (newtype.type == CodeConstants.TYPE_OBJECT && newtype.arraydim == 0) {
+ ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(newtype.value);
+
+ if (node != null && (node.type == ClassNode.CLASS_ANONYMOUS || node.type == ClassNode.CLASS_LAMBDA)) {
+ anonymous = true;
+
+ if (node.type == ClassNode.CLASS_LAMBDA) {
+ lambda = true;
+ }
+ }
+ }
+ }
+
+ public VarType getExprType() {
+
+ if (anonymous) {
+ ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(newtype.value);
+
+ return node.anonimousClassType;
+ }
+ else {
+ return newtype;
+ }
+ }
+
+ public CheckTypesResult checkExprTypeBounds() {
+ CheckTypesResult result = new CheckTypesResult();
+
+ if (newtype.arraydim != 0) {
+ for (Exprent dim : lstDims) {
+ result.addMinTypeExprent(dim, VarType.VARTYPE_BYTECHAR);
+ result.addMaxTypeExprent(dim, VarType.VARTYPE_INT);
+ }
+
+ if (newtype.arraydim == 1) {
+
+ VarType leftType = newtype.copy();
+ leftType.decArrayDim();
+
+ for (Exprent element : lstArrayElements) {
+ result.addMinTypeExprent(element, VarType.getMinTypeInFamily(leftType.type_family));
+ result.addMaxTypeExprent(element, leftType);
+ }
+ }
+ }
+ else {
+ if (constructor != null) {
+ return constructor.checkExprTypeBounds();
+ }
+ }
+
+ return result;
+ }
+
+ public List<Exprent> getAllExprents() {
+ List<Exprent> lst = new ArrayList<Exprent>();
+ if (newtype.arraydim == 0) {
+ if (constructor != null) {
+ Exprent constructor_instance = constructor.getInstance();
+
+ if (constructor_instance != null) { // should be true only for a lambda expression with a virtual content method
+ lst.add(constructor_instance);
+ }
+
+ lst.addAll(constructor.getLstParameters());
+ }
+ }
+ else {
+ lst.addAll(lstDims);
+ lst.addAll(lstArrayElements);
+ }
+
+ return lst;
+ }
+
+ public Exprent copy() {
+ List<Exprent> lst = new ArrayList<Exprent>();
+ for (Exprent expr : lstDims) {
+ lst.add(expr.copy());
+ }
+
+ NewExprent ret = new NewExprent(newtype, lst);
+ ret.setConstructor(constructor == null ? null : (InvocationExprent)constructor.copy());
+ ret.setLstArrayElements(lstArrayElements);
+ ret.setDirectArrayInit(directArrayInit);
+ ret.setAnonymous(anonymous);
+ ret.setEnumconst(enumconst);
+ return ret;
+ }
+
+ public int getPrecedence() {
+ return 1; // precedence of new
+ }
+
+ public String toJava(int indent) {
+ StringBuilder buf = new StringBuilder();
+
+ if (anonymous) {
+
+ ClassNode child = DecompilerContext.getClassProcessor().getMapRootClasses().get(newtype.value);
+
+ buf.append("(");
+
+ if (!lambda && constructor != null) {
+
+ InvocationExprent invsuper = child.superInvocation;
+
+ ClassNode newnode = DecompilerContext.getClassProcessor().getMapRootClasses().get(invsuper.getClassname());
+
+ List<VarVersionPaar> sigFields = null;
+ if (newnode != null) { // own class
+ if (newnode.wrapper != null) {
+ sigFields = newnode.wrapper.getMethodWrapper("<init>", invsuper.getStringDescriptor()).signatureFields;
+ }
+ else {
+ if (newnode.type == ClassNode.CLASS_MEMBER && (newnode.access & CodeConstants.ACC_STATIC) == 0 &&
+ !constructor.getLstParameters().isEmpty()) { // member non-static class invoked with enclosing class instance
+ sigFields = new ArrayList<VarVersionPaar>(Collections.nCopies(constructor.getLstParameters().size(), (VarVersionPaar)null));
+ sigFields.set(0, new VarVersionPaar(-1, 0));
+ }
+ }
+ }
+
+ boolean firstpar = true;
+ int start = 0, end = invsuper.getLstParameters().size();
+ if (enumconst) {
+ start += 2;
+ end -= 1;
+ }
+ for (int i = start; i < end; i++) {
+ if (sigFields == null || sigFields.get(i) == null) {
+ if (!firstpar) {
+ buf.append(", ");
+ }
+
+ Exprent param = invsuper.getLstParameters().get(i);
+ if (param.type == Exprent.EXPRENT_VAR) {
+ int varindex = ((VarExprent)param).getIndex();
+ if (varindex > 0 && varindex <= constructor.getLstParameters().size()) {
+ param = constructor.getLstParameters().get(varindex - 1);
+ }
+ }
+
+ StringBuilder buff = new StringBuilder();
+ ExprProcessor.getCastedExprent(param, invsuper.getDescriptor().params[i], buff, indent, true);
+
+ buf.append(buff);
+ firstpar = false;
+ }
+ }
+ }
+
+ if (!enumconst) {
+ String enclosing = null;
+ if (!lambda && constructor != null) {
+ enclosing = getQualifiedNewInstance(child.anonimousClassType.value, constructor.getLstParameters(), indent);
+ }
+
+ String typename = ExprProcessor.getCastTypeName(child.anonimousClassType);
+
+ if (enclosing != null) {
+ ClassNode anonimousNode = DecompilerContext.getClassProcessor().getMapRootClasses().get(child.anonimousClassType.value);
+ if (anonimousNode != null) {
+ typename = anonimousNode.simpleName;
+ }
+ else {
+ typename = typename.substring(typename.lastIndexOf('.') + 1);
+ }
+ }
+ buf.insert(0, "new " + typename);
+
+ if (enclosing != null) {
+ buf.insert(0, enclosing + ".");
+ }
+ }
+
+ buf.append(")");
+
+ if (enumconst && buf.length() == 2) {
+ buf.setLength(0);
+ }
+
+ if (lambda) {
+ if (!DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS)) {
+ buf.setLength(0); // remove the usual 'new <class>()', it will be replaced with lambda style '() ->'
+ }
+ Exprent methodObject = constructor == null ? null : constructor.getInstance();
+ new ClassWriter().classLambdaToJava(child, buf, methodObject, indent);
+ }
+ else {
+ new ClassWriter().classToJava(child, buf, indent);
+ }
+ }
+ else if (directArrayInit) {
+ VarType leftType = newtype.copy();
+ leftType.decArrayDim();
+
+ buf.append("{");
+ for (int i = 0; i < lstArrayElements.size(); i++) {
+ if (i > 0) {
+ buf.append(", ");
+ }
+ ExprProcessor.getCastedExprent(lstArrayElements.get(i), leftType, buf, indent, false);
+ }
+ buf.append("}");
+ }
+ else {
+ if (newtype.arraydim == 0) {
+
+ if (constructor != null) {
+
+ List<Exprent> lstParameters = constructor.getLstParameters();
+
+ ClassNode newnode = DecompilerContext.getClassProcessor().getMapRootClasses().get(constructor.getClassname());
+
+ List<VarVersionPaar> sigFields = null;
+ if (newnode != null) { // own class
+ if (newnode.wrapper != null) {
+ sigFields = newnode.wrapper.getMethodWrapper("<init>", constructor.getStringDescriptor()).signatureFields;
+ }
+ else {
+ if (newnode.type == ClassNode.CLASS_MEMBER && (newnode.access & CodeConstants.ACC_STATIC) == 0 &&
+ !constructor.getLstParameters().isEmpty()) { // member non-static class invoked with enclosing class instance
+ sigFields = new ArrayList<VarVersionPaar>(Collections.nCopies(lstParameters.size(), (VarVersionPaar)null));
+ sigFields.set(0, new VarVersionPaar(-1, 0));
+ }
+ }
+ }
+
+ int start = enumconst ? 2 : 0;
+ if (!enumconst || start < lstParameters.size()) {
+ buf.append("(");
+
+ boolean firstpar = true;
+ for (int i = start; i < lstParameters.size(); i++) {
+ if (sigFields == null || sigFields.get(i) == null) {
+ if (!firstpar) {
+ buf.append(", ");
+ }
+
+ StringBuilder buff = new StringBuilder();
+ ExprProcessor.getCastedExprent(lstParameters.get(i), constructor.getDescriptor().params[i], buff, indent, true);
+
+ buf.append(buff);
+ firstpar = false;
+ }
+ }
+ buf.append(")");
+ }
+ }
+
+ if (!enumconst) {
+ String enclosing = null;
+ if (constructor != null) {
+ enclosing = getQualifiedNewInstance(newtype.value, constructor.getLstParameters(), indent);
+ }
+
+ String typename = ExprProcessor.getTypeName(newtype);
+
+ if (enclosing != null) {
+ ClassNode newNode = DecompilerContext.getClassProcessor().getMapRootClasses().get(newtype.value);
+ if (newNode != null) {
+ typename = newNode.simpleName;
+ }
+ else {
+ typename = typename.substring(typename.lastIndexOf('.') + 1);
+ }
+ }
+ buf.insert(0, "new " + typename);
+
+ if (enclosing != null) {
+ buf.insert(0, enclosing + ".");
+ }
+ }
+ }
+ else {
+ buf.append("new ").append(ExprProcessor.getTypeName(newtype));
+
+ if (lstArrayElements.isEmpty()) {
+ for (int i = 0; i < newtype.arraydim; i++) {
+ buf.append("[").append(i < lstDims.size() ? lstDims.get(i).toJava(indent) : "").append("]");
+ }
+ }
+ else {
+ for (int i = 0; i < newtype.arraydim; i++) {
+ buf.append("[]");
+ }
+
+ VarType leftType = newtype.copy();
+ leftType.decArrayDim();
+
+ buf.append("{");
+ for (int i = 0; i < lstArrayElements.size(); i++) {
+ if (i > 0) {
+ buf.append(", ");
+ }
+ StringBuilder buff = new StringBuilder();
+ ExprProcessor.getCastedExprent(lstArrayElements.get(i), leftType, buff, indent, false);
+
+ buf.append(buff);
+ }
+ buf.append("}");
+ }
+ }
+ }
+ return buf.toString();
+ }
+
+ private static String getQualifiedNewInstance(String classname, List<Exprent> lstParams, int indent) {
+
+ ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(classname);
+
+ if (node != null && node.type != ClassNode.CLASS_ROOT && (node.access & CodeConstants.ACC_STATIC) == 0) {
+ if (!lstParams.isEmpty()) {
+ Exprent enclosing = lstParams.get(0);
+
+ boolean isQualifiedNew = false;
+
+ if (enclosing.type == Exprent.EXPRENT_VAR) {
+ VarExprent varEnclosing = (VarExprent)enclosing;
+
+ StructClass current_class = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE)).classStruct;
+ String this_classname = varEnclosing.getProcessor().getThisvars().get(new VarVersionPaar(varEnclosing));
+
+ if (!current_class.qualifiedName.equals(this_classname)) {
+ isQualifiedNew = true;
+ }
+ }
+ else {
+ isQualifiedNew = true;
+ }
+
+ if (isQualifiedNew) {
+ return enclosing.toJava(indent);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (o == null || !(o instanceof NewExprent)) return false;
+
+ NewExprent ne = (NewExprent)o;
+ return InterpreterUtil.equalObjects(newtype, ne.getNewtype()) &&
+ InterpreterUtil.equalLists(lstDims, ne.getLstDims()) &&
+ InterpreterUtil.equalObjects(constructor, ne.getConstructor()) &&
+ directArrayInit == ne.directArrayInit &&
+ InterpreterUtil.equalLists(lstArrayElements, ne.getLstArrayElements());
+ }
+
+ public void replaceExprent(Exprent oldexpr, Exprent newexpr) {
+ if (oldexpr == constructor) {
+ constructor = (InvocationExprent)newexpr;
+ }
+
+ if (constructor != null) {
+ constructor.replaceExprent(oldexpr, newexpr);
+ }
+
+ for (int i = 0; i < lstDims.size(); i++) {
+ if (oldexpr == lstDims.get(i)) {
+ lstDims.set(i, newexpr);
+ }
+ }
+
+ for (int i = 0; i < lstArrayElements.size(); i++) {
+ if (oldexpr == lstArrayElements.get(i)) {
+ lstArrayElements.set(i, newexpr);
+ }
+ }
+ }
+
+ public InvocationExprent getConstructor() {
+ return constructor;
+ }
+
+ public void setConstructor(InvocationExprent constructor) {
+ this.constructor = constructor;
+ }
+
+ public List<Exprent> getLstDims() {
+ return lstDims;
+ }
+
+ public VarType getNewtype() {
+ return newtype;
+ }
+
+ public List<Exprent> getLstArrayElements() {
+ return lstArrayElements;
+ }
+
+ public void setLstArrayElements(List<Exprent> lstArrayElements) {
+ this.lstArrayElements = lstArrayElements;
+ }
+
+ public boolean isDirectArrayInit() {
+ return directArrayInit;
+ }
+
+ public void setDirectArrayInit(boolean directArrayInit) {
+ this.directArrayInit = directArrayInit;
+ }
+
+ public boolean isLambda() {
+ return lambda;
+ }
+
+ public boolean isAnonymous() {
+ return anonymous;
+ }
+
+ public void setAnonymous(boolean anonymous) {
+ this.anonymous = anonymous;
+ }
+
+ public boolean isEnumconst() {
+ return enumconst;
+ }
+
+ public void setEnumconst(boolean enumconst) {
+ this.enumconst = enumconst;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/SwitchExprent.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/SwitchExprent.java
new file mode 100644
index 000000000000..36bead20490d
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/SwitchExprent.java
@@ -0,0 +1,114 @@
+/*
+ * 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.modules.decompiler.exps;
+
+import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class SwitchExprent extends Exprent {
+
+ private Exprent value;
+
+ private List<List<ConstExprent>> caseValues = new ArrayList<List<ConstExprent>>();
+
+ {
+ this.type = EXPRENT_SWITCH;
+ }
+
+ public SwitchExprent(Exprent value) {
+ this.value = value;
+ }
+
+ public Exprent copy() {
+ SwitchExprent swexpr = new SwitchExprent(value.copy());
+
+ List<List<ConstExprent>> lstCaseValues = new ArrayList<List<ConstExprent>>();
+ for (List<ConstExprent> lst : caseValues) {
+ lstCaseValues.add(new ArrayList<ConstExprent>(lst));
+ }
+ swexpr.setCaseValues(lstCaseValues);
+
+ return swexpr;
+ }
+
+ public VarType getExprType() {
+ return value.getExprType();
+ }
+
+ public CheckTypesResult checkExprTypeBounds() {
+ CheckTypesResult result = new CheckTypesResult();
+
+ result.addMinTypeExprent(value, VarType.VARTYPE_BYTECHAR);
+ result.addMaxTypeExprent(value, VarType.VARTYPE_INT);
+
+ VarType valtype = value.getExprType();
+ for (List<ConstExprent> lst : caseValues) {
+ for (ConstExprent expr : lst) {
+ if (expr != null) {
+ VarType casetype = expr.getExprType();
+ if (!casetype.equals(valtype)) {
+ valtype = VarType.getCommonSupertype(casetype, valtype);
+ result.addMinTypeExprent(value, valtype);
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ public List<Exprent> getAllExprents() {
+ List<Exprent> lst = new ArrayList<Exprent>();
+ lst.add(value);
+ return lst;
+ }
+
+ public String toJava(int indent) {
+ return "switch(" + value.toJava(indent) + ")";
+ }
+
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+
+ if (o == null || !(o instanceof SwitchExprent)) {
+ return false;
+ }
+
+ SwitchExprent sw = (SwitchExprent)o;
+ return InterpreterUtil.equalObjects(value, sw.getValue());
+ }
+
+ public void replaceExprent(Exprent oldexpr, Exprent newexpr) {
+ if (oldexpr == value) {
+ value = newexpr;
+ }
+ }
+
+ public Exprent getValue() {
+ return value;
+ }
+
+ public void setCaseValues(List<List<ConstExprent>> caseValues) {
+ this.caseValues = caseValues;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java
new file mode 100644
index 000000000000..352350171a7a
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java
@@ -0,0 +1,182 @@
+/*
+ * 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.modules.decompiler.exps;
+
+import org.jetbrains.java.decompiler.code.CodeConstants;
+import org.jetbrains.java.decompiler.main.ClassWriter;
+import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class VarExprent extends Exprent {
+
+ public static final int STACK_BASE = 10000;
+
+ public static final String VAR_NAMELESS_ENCLOSURE = "<VAR_NAMELESS_ENCLOSURE>";
+
+ private int index;
+
+ private VarType vartype;
+
+ private boolean definition = false;
+
+ private VarProcessor processor;
+
+ private int version = 0;
+
+ private boolean classdef = false;
+
+ private boolean stack = false;
+
+ {
+ this.type = EXPRENT_VAR;
+ }
+
+ public VarExprent(int index, VarType vartype, VarProcessor processor) {
+ this.index = index;
+ this.vartype = vartype;
+ this.processor = processor;
+ }
+
+ public VarType getExprType() {
+ return getVartype();
+ }
+
+ public int getExprentUse() {
+ return Exprent.MULTIPLE_USES | Exprent.SIDE_EFFECTS_FREE;
+ }
+
+ public List<Exprent> getAllExprents() {
+ return new ArrayList<Exprent>();
+ }
+
+ public Exprent copy() {
+ VarExprent var = new VarExprent(index, getVartype(), processor);
+ var.setDefinition(definition);
+ var.setVersion(version);
+ var.setClassdef(classdef);
+ var.setStack(stack);
+ return var;
+ }
+
+ public String toJava(int indent) {
+ StringBuilder buffer = new StringBuilder();
+
+ if (classdef) {
+ ClassNode child = DecompilerContext.getClassProcessor().getMapRootClasses().get(vartype.value);
+ new ClassWriter().classToJava(child, buffer, indent);
+ }
+ else {
+ String name = null;
+ if (processor != null) {
+ name = processor.getVarName(new VarVersionPaar(index, version));
+ }
+
+ if (definition) {
+ if (processor != null && processor.getVarFinal(new VarVersionPaar(index, version)) == VarTypeProcessor.VAR_FINALEXPLICIT) {
+ buffer.append("final ");
+ }
+ buffer.append(ExprProcessor.getCastTypeName(getVartype())).append(" ");
+ }
+ buffer.append(name == null ? ("var" + index + (version == 0 ? "" : "_" + version)) : name);
+ }
+
+ return buffer.toString();
+ }
+
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (o == null || !(o instanceof VarExprent)) return false;
+
+ VarExprent ve = (VarExprent)o;
+ return index == ve.getIndex() &&
+ version == ve.getVersion() &&
+ InterpreterUtil.equalObjects(getVartype(), ve.getVartype()); // FIXME: vartype comparison redundant?
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public void setIndex(int index) {
+ this.index = index;
+ }
+
+ public VarType getVartype() {
+ VarType vt = null;
+ if (processor != null) {
+ vt = processor.getVarType(new VarVersionPaar(index, version));
+ }
+
+ if (vt == null || (vartype != null && vartype.type != CodeConstants.TYPE_UNKNOWN)) {
+ vt = vartype;
+ }
+
+ return vt == null ? VarType.VARTYPE_UNKNOWN : vt;
+ }
+
+ public void setVartype(VarType vartype) {
+ this.vartype = vartype;
+ }
+
+ public boolean isDefinition() {
+ return definition;
+ }
+
+ public void setDefinition(boolean definition) {
+ this.definition = definition;
+ }
+
+ public VarProcessor getProcessor() {
+ return processor;
+ }
+
+ public void setProcessor(VarProcessor processor) {
+ this.processor = processor;
+ }
+
+ public int getVersion() {
+ return version;
+ }
+
+ public void setVersion(int version) {
+ this.version = version;
+ }
+
+ public boolean isClassdef() {
+ return classdef;
+ }
+
+ public void setClassdef(boolean classdef) {
+ this.classdef = classdef;
+ }
+
+ public boolean isStack() {
+ return stack;
+ }
+
+ public void setStack(boolean stack) {
+ this.stack = stack;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectGraph.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectGraph.java
new file mode 100644
index 000000000000..b24b72c261ea
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectGraph.java
@@ -0,0 +1,136 @@
+/*
+ * 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.modules.decompiler.sforms;
+
+import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
+import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper.FinallyPathWrapper;
+import org.jetbrains.java.decompiler.util.VBStyleCollection;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+
+
+public class DirectGraph {
+
+ public VBStyleCollection<DirectNode, String> nodes = new VBStyleCollection<DirectNode, String>();
+
+ public DirectNode first;
+
+ // exit, [source, destination]
+ public HashMap<String, List<FinallyPathWrapper>> mapShortRangeFinallyPaths = new HashMap<String, List<FinallyPathWrapper>>();
+
+ // exit, [source, destination]
+ public HashMap<String, List<FinallyPathWrapper>> mapLongRangeFinallyPaths = new HashMap<String, List<FinallyPathWrapper>>();
+
+ // negative if branches (recorded for handling of && and ||)
+ public HashMap<String, String> mapNegIfBranch = new HashMap<String, String>();
+
+ // nodes, that are exception exits of a finally block with monitor variable
+ public HashMap<String, String> mapFinallyMonitorExceptionPathExits = new HashMap<String, String>();
+
+ public void sortReversePostOrder() {
+ LinkedList<DirectNode> res = new LinkedList<DirectNode>();
+ addToReversePostOrderListIterative(first, res);
+
+ nodes.clear();
+ for (DirectNode node : res) {
+ nodes.addWithKey(node, node.id);
+ }
+ }
+
+ private static void addToReversePostOrderListIterative(DirectNode root, List<DirectNode> lst) {
+
+ LinkedList<DirectNode> stackNode = new LinkedList<DirectNode>();
+ LinkedList<Integer> stackIndex = new LinkedList<Integer>();
+
+ HashSet<DirectNode> setVisited = new HashSet<DirectNode>();
+
+ stackNode.add(root);
+ stackIndex.add(0);
+
+ while (!stackNode.isEmpty()) {
+
+ DirectNode node = stackNode.getLast();
+ int index = stackIndex.removeLast();
+
+ setVisited.add(node);
+
+ for (; index < node.succs.size(); index++) {
+ DirectNode succ = node.succs.get(index);
+
+ if (!setVisited.contains(succ)) {
+ stackIndex.add(index + 1);
+
+ stackNode.add(succ);
+ stackIndex.add(0);
+
+ break;
+ }
+ }
+
+ if (index == node.succs.size()) {
+ lst.add(0, node);
+
+ stackNode.removeLast();
+ }
+ }
+ }
+
+
+ public boolean iterateExprents(ExprentIterator iter) {
+
+ LinkedList<DirectNode> stack = new LinkedList<DirectNode>();
+ stack.add(first);
+
+ HashSet<DirectNode> setVisited = new HashSet<DirectNode>();
+
+ while (!stack.isEmpty()) {
+
+ DirectNode node = stack.removeFirst();
+
+ if (setVisited.contains(node)) {
+ continue;
+ }
+ setVisited.add(node);
+
+ for (int i = 0; i < node.exprents.size(); i++) {
+ int res = iter.processExprent(node.exprents.get(i));
+
+ if (res == 1) {
+ return false;
+ }
+
+ if (res == 2) {
+ node.exprents.remove(i);
+ i--;
+ }
+ }
+
+ stack.addAll(node.succs);
+ }
+
+ return true;
+ }
+
+ public interface ExprentIterator {
+ // 0 - success, do nothing
+ // 1 - cancel iteration
+ // 2 - success, delete exprent
+ int processExprent(Exprent exprent);
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectNode.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectNode.java
new file mode 100644
index 000000000000..73303ec6a6d4
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectNode.java
@@ -0,0 +1,67 @@
+/*
+ * 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.modules.decompiler.sforms;
+
+import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class DirectNode {
+
+ public static final int NODE_DIRECT = 1;
+ public static final int NODE_TAIL = 2;
+ public static final int NODE_INIT = 3;
+ public static final int NODE_CONDITION = 4;
+ public static final int NODE_INCREMENT = 5;
+ public static final int NODE_TRY = 6;
+
+ public int type;
+
+ public String id;
+
+ public BasicBlockStatement block;
+
+ public Statement statement;
+
+ public List<Exprent> exprents = new ArrayList<Exprent>();
+
+ public List<DirectNode> succs = new ArrayList<DirectNode>();
+
+ public List<DirectNode> preds = new ArrayList<DirectNode>();
+
+ public DirectNode(int type, Statement statement, String id) {
+ this.type = type;
+ this.statement = statement;
+ this.id = id;
+ }
+
+ public DirectNode(int type, Statement statement, BasicBlockStatement block) {
+ this.type = type;
+ this.statement = statement;
+
+ this.id = block.id.toString();
+ this.block = block;
+ }
+
+ @Override
+ public String toString() {
+ return id;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java
new file mode 100644
index 000000000000..05d2f33b27e5
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java
@@ -0,0 +1,574 @@
+/*
+ * 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.modules.decompiler.sforms;
+
+import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.*;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+
+public class FlattenStatementsHelper {
+
+ // statement.id, node.id(direct), node.id(continue)
+ private Map<Integer, String[]> mapDestinationNodes = new HashMap<Integer, String[]>();
+
+ // node.id(source), statement.id(destination), edge type
+ private List<Edge> listEdges = new ArrayList<Edge>();
+
+ // node.id(exit), [node.id(source), statement.id(destination)]
+ private Map<String, List<String[]>> mapShortRangeFinallyPathIds = new HashMap<String, List<String[]>>();
+
+ // node.id(exit), [node.id(source), statement.id(destination)]
+ private Map<String, List<String[]>> mapLongRangeFinallyPathIds = new HashMap<String, List<String[]>>();
+
+ // positive if branches
+ private Map<String, Integer> mapPosIfBranch = new HashMap<String, Integer>();
+
+ private DirectGraph graph;
+
+ private RootStatement root;
+
+ public DirectGraph buildDirectGraph(RootStatement root) {
+
+ this.root = root;
+
+ graph = new DirectGraph();
+
+ flattenStatement();
+
+ // dummy exit node
+ Statement dummyexit = root.getDummyExit();
+ DirectNode node = new DirectNode(DirectNode.NODE_DIRECT, dummyexit, dummyexit.id.toString());
+ node.exprents = new ArrayList<Exprent>();
+ graph.nodes.addWithKey(node, node.id);
+ mapDestinationNodes.put(dummyexit.id, new String[]{node.id, null});
+
+ setEdges();
+
+ graph.first = graph.nodes.getWithKey(mapDestinationNodes.get(root.id)[0]);
+ graph.sortReversePostOrder();
+
+ return graph;
+ }
+
+ private void flattenStatement() {
+
+ class StatementStackEntry {
+ public Statement statement;
+ public LinkedList<StackEntry> stackFinally;
+ public List<Exprent> tailExprents;
+
+ public int statementIndex;
+ public int edgeIndex;
+ public List<StatEdge> succEdges;
+
+ public StatementStackEntry(Statement statement, LinkedList<StackEntry> stackFinally, List<Exprent> tailExprents) {
+ this.statement = statement;
+ this.stackFinally = stackFinally;
+ this.tailExprents = tailExprents;
+ }
+ }
+
+ LinkedList<StatementStackEntry> lstStackStatements = new LinkedList<StatementStackEntry>();
+
+ lstStackStatements.add(new StatementStackEntry(root, new LinkedList<StackEntry>(), null));
+
+ mainloop:
+ while (!lstStackStatements.isEmpty()) {
+
+ StatementStackEntry statEntry = lstStackStatements.removeFirst();
+
+ Statement stat = statEntry.statement;
+ LinkedList<StackEntry> stackFinally = statEntry.stackFinally;
+ int statementBreakIndex = statEntry.statementIndex;
+
+ DirectNode node, nd;
+
+ List<StatEdge> lstSuccEdges = new ArrayList<StatEdge>();
+ DirectNode sourcenode = null;
+
+ if (statEntry.succEdges == null) {
+
+ switch (stat.type) {
+ case Statement.TYPE_BASICBLOCK:
+ node = new DirectNode(DirectNode.NODE_DIRECT, stat, (BasicBlockStatement)stat);
+ if (stat.getExprents() != null) {
+ node.exprents = stat.getExprents();
+ }
+ graph.nodes.putWithKey(node, node.id);
+ mapDestinationNodes.put(stat.id, new String[]{node.id, null});
+
+ lstSuccEdges.addAll(stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL));
+ sourcenode = node;
+
+ List<Exprent> tailExprentList = statEntry.tailExprents;
+
+ if (tailExprentList != null) {
+ DirectNode tail = new DirectNode(DirectNode.NODE_TAIL, stat, stat.id + "_tail");
+ tail.exprents = tailExprentList;
+ graph.nodes.putWithKey(tail, tail.id);
+
+ mapDestinationNodes.put(-stat.id, new String[]{tail.id, null});
+ listEdges.add(new Edge(node.id, -stat.id, StatEdge.TYPE_REGULAR));
+
+ sourcenode = tail;
+ }
+
+ // 'if' statement: record positive branch
+ if (stat.getLastBasicType() == Statement.LASTBASICTYPE_IF) {
+ mapPosIfBranch.put(sourcenode.id, lstSuccEdges.get(0).getDestination().id);
+ }
+
+ break;
+ case Statement.TYPE_CATCHALL:
+ case Statement.TYPE_TRYCATCH:
+ DirectNode firstnd = new DirectNode(DirectNode.NODE_TRY, stat, stat.id + "_try");
+
+ mapDestinationNodes.put(stat.id, new String[]{firstnd.id, null});
+ graph.nodes.putWithKey(firstnd, firstnd.id);
+
+ LinkedList<StatementStackEntry> lst = new LinkedList<StatementStackEntry>();
+
+ for (Statement st : stat.getStats()) {
+ listEdges.add(new Edge(firstnd.id, st.id, StatEdge.TYPE_REGULAR));
+
+ LinkedList<StackEntry> stack = stackFinally;
+ if (stat.type == Statement.TYPE_CATCHALL && ((CatchAllStatement)stat).isFinally()) {
+ stack = new LinkedList<StackEntry>(stackFinally);
+
+ if (st == stat.getFirst()) { // catch head
+ stack.add(new StackEntry((CatchAllStatement)stat, Boolean.FALSE));
+ }
+ else { // handler
+ stack.add(new StackEntry((CatchAllStatement)stat, Boolean.TRUE, StatEdge.TYPE_BREAK,
+ root.getDummyExit(), st, st, firstnd, firstnd, true));
+ }
+ }
+ lst.add(new StatementStackEntry(st, stack, null));
+ }
+
+ lstStackStatements.addAll(0, lst);
+ break;
+ case Statement.TYPE_DO:
+ if (statementBreakIndex == 0) {
+ statEntry.statementIndex = 1;
+ lstStackStatements.addFirst(statEntry);
+ lstStackStatements.addFirst(new StatementStackEntry(stat.getFirst(), stackFinally, null));
+
+ continue mainloop;
+ }
+
+ nd = graph.nodes.getWithKey(mapDestinationNodes.get(stat.getFirst().id)[0]);
+
+ DoStatement dostat = (DoStatement)stat;
+ int looptype = dostat.getLooptype();
+
+ if (looptype == DoStatement.LOOP_DO) {
+ mapDestinationNodes.put(stat.id, new String[]{nd.id, nd.id});
+ break;
+ }
+
+ lstSuccEdges.add(stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL).get(0)); // exactly one edge
+
+ switch (looptype) {
+ case DoStatement.LOOP_WHILE:
+ case DoStatement.LOOP_DOWHILE:
+ node = new DirectNode(DirectNode.NODE_CONDITION, stat, stat.id + "_cond");
+ node.exprents = dostat.getConditionExprentList();
+ graph.nodes.putWithKey(node, node.id);
+
+ listEdges.add(new Edge(node.id, stat.getFirst().id, StatEdge.TYPE_REGULAR));
+
+ if (looptype == DoStatement.LOOP_WHILE) {
+ mapDestinationNodes.put(stat.id, new String[]{node.id, node.id});
+ }
+ else {
+ mapDestinationNodes.put(stat.id, new String[]{nd.id, node.id});
+
+ boolean found = false;
+ for (Edge edge : listEdges) {
+ if (edge.statid.equals(stat.id) && edge.edgetype == StatEdge.TYPE_CONTINUE) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ listEdges.add(new Edge(nd.id, stat.id, StatEdge.TYPE_CONTINUE));
+ }
+ }
+ sourcenode = node;
+ break;
+ case DoStatement.LOOP_FOR:
+ DirectNode nodeinit = new DirectNode(DirectNode.NODE_INIT, stat, stat.id + "_init");
+ if (dostat.getInitExprent() != null) {
+ nodeinit.exprents = dostat.getInitExprentList();
+ }
+ graph.nodes.putWithKey(nodeinit, nodeinit.id);
+
+ DirectNode nodecond = new DirectNode(DirectNode.NODE_CONDITION, stat, stat.id + "_cond");
+ nodecond.exprents = dostat.getConditionExprentList();
+ graph.nodes.putWithKey(nodecond, nodecond.id);
+
+ DirectNode nodeinc = new DirectNode(DirectNode.NODE_INCREMENT, stat, stat.id + "_inc");
+ nodeinc.exprents = dostat.getIncExprentList();
+ graph.nodes.putWithKey(nodeinc, nodeinc.id);
+
+ mapDestinationNodes.put(stat.id, new String[]{nodeinit.id, nodeinc.id});
+ mapDestinationNodes.put(-stat.id, new String[]{nodecond.id, null});
+
+ listEdges.add(new Edge(nodecond.id, stat.getFirst().id, StatEdge.TYPE_REGULAR));
+ listEdges.add(new Edge(nodeinit.id, -stat.id, StatEdge.TYPE_REGULAR));
+ listEdges.add(new Edge(nodeinc.id, -stat.id, StatEdge.TYPE_REGULAR));
+
+ boolean found = false;
+ for (Edge edge : listEdges) {
+ if (edge.statid.equals(stat.id) && edge.edgetype == StatEdge.TYPE_CONTINUE) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ listEdges.add(new Edge(nd.id, stat.id, StatEdge.TYPE_CONTINUE));
+ }
+
+ sourcenode = nodecond;
+ }
+ break;
+ case Statement.TYPE_SYNCRONIZED:
+ case Statement.TYPE_SWITCH:
+ case Statement.TYPE_IF:
+ case Statement.TYPE_SEQUENCE:
+ case Statement.TYPE_ROOT:
+ int statsize = stat.getStats().size();
+ if (stat.type == Statement.TYPE_SYNCRONIZED) {
+ statsize = 2; // exclude the handler if synchronized
+ }
+
+ if (statementBreakIndex <= statsize) {
+ List<Exprent> tailexprlst = null;
+
+ switch (stat.type) {
+ case Statement.TYPE_SYNCRONIZED:
+ tailexprlst = ((SynchronizedStatement)stat).getHeadexprentList();
+ break;
+ case Statement.TYPE_SWITCH:
+ tailexprlst = ((SwitchStatement)stat).getHeadexprentList();
+ break;
+ case Statement.TYPE_IF:
+ tailexprlst = ((IfStatement)stat).getHeadexprentList();
+ }
+
+ for (int i = statementBreakIndex; i < statsize; i++) {
+ statEntry.statementIndex = i + 1;
+ lstStackStatements.addFirst(statEntry);
+ lstStackStatements.addFirst(
+ new StatementStackEntry(stat.getStats().get(i), stackFinally,
+ (i == 0 && tailexprlst != null && tailexprlst.get(0) != null) ? tailexprlst : null));
+
+ continue mainloop;
+ }
+
+ node = graph.nodes.getWithKey(mapDestinationNodes.get(stat.getFirst().id)[0]);
+ mapDestinationNodes.put(stat.id, new String[]{node.id, null});
+
+ if (stat.type == Statement.TYPE_IF && ((IfStatement)stat).iftype == IfStatement.IFTYPE_IF) {
+ lstSuccEdges.add(stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL).get(0)); // exactly one edge
+ sourcenode = tailexprlst.get(0) == null ? node : graph.nodes.getWithKey(node.id + "_tail");
+ }
+ }
+ }
+ }
+
+ // no successor edges
+ if (sourcenode != null) {
+
+ if (statEntry.succEdges != null) {
+ lstSuccEdges = statEntry.succEdges;
+ }
+
+ for (int edgeindex = statEntry.edgeIndex; edgeindex < lstSuccEdges.size(); edgeindex++) {
+
+ StatEdge edge = lstSuccEdges.get(edgeindex);
+
+ LinkedList<StackEntry> stack = new LinkedList<StackEntry>(stackFinally);
+
+ int edgetype = edge.getType();
+ Statement destination = edge.getDestination();
+
+ DirectNode finallyShortRangeSource = sourcenode;
+ DirectNode finallyLongRangeSource = sourcenode;
+ Statement finallyShortRangeEntry = null;
+ Statement finallyLongRangeEntry = null;
+
+ boolean isFinallyMonitorExceptionPath = false;
+
+ boolean isFinallyExit = false;
+
+ while (true) {
+
+ StackEntry entry = null;
+ if (!stack.isEmpty()) {
+ entry = stack.getLast();
+ }
+
+ boolean created = true;
+
+ if (entry == null) {
+ saveEdge(sourcenode, destination, edgetype, isFinallyExit ? finallyShortRangeSource : null, finallyLongRangeSource,
+ finallyShortRangeEntry, finallyLongRangeEntry, isFinallyMonitorExceptionPath);
+ }
+ else {
+
+ CatchAllStatement catchall = entry.catchstatement;
+
+ if (entry.state) { // finally handler statement
+ if (edgetype == StatEdge.TYPE_FINALLYEXIT) {
+
+ stack.removeLast();
+ destination = entry.destination;
+ edgetype = entry.edgetype;
+
+ finallyShortRangeSource = entry.finallyShortRangeSource;
+ finallyLongRangeSource = entry.finallyLongRangeSource;
+ finallyShortRangeEntry = entry.finallyShortRangeEntry;
+ finallyLongRangeEntry = entry.finallyLongRangeEntry;
+
+ isFinallyExit = true;
+ isFinallyMonitorExceptionPath = (catchall.getMonitor() != null) & entry.isFinallyExceptionPath;
+
+ created = false;
+ }
+ else {
+ if (!catchall.containsStatementStrict(destination)) {
+ stack.removeLast();
+ created = false;
+ }
+ else {
+ saveEdge(sourcenode, destination, edgetype, isFinallyExit ? finallyShortRangeSource : null, finallyLongRangeSource,
+ finallyShortRangeEntry, finallyLongRangeEntry, isFinallyMonitorExceptionPath);
+ }
+ }
+ }
+ else { // finally protected try statement
+ if (!catchall.containsStatementStrict(destination)) {
+ saveEdge(sourcenode, catchall.getHandler(), StatEdge.TYPE_REGULAR, isFinallyExit ? finallyShortRangeSource : null,
+ finallyLongRangeSource, finallyShortRangeEntry, finallyLongRangeEntry, isFinallyMonitorExceptionPath);
+
+ stack.removeLast();
+ stack.add(new StackEntry(catchall, Boolean.TRUE, edgetype, destination, catchall.getHandler(),
+ finallyLongRangeEntry == null ? catchall.getHandler() : finallyLongRangeEntry,
+ sourcenode, finallyLongRangeSource, false));
+
+ statEntry.edgeIndex = edgeindex + 1;
+ statEntry.succEdges = lstSuccEdges;
+ lstStackStatements.addFirst(statEntry);
+ lstStackStatements.addFirst(new StatementStackEntry(catchall.getHandler(), stack, null));
+
+ continue mainloop;
+ }
+ else {
+ saveEdge(sourcenode, destination, edgetype, isFinallyExit ? finallyShortRangeSource : null, finallyLongRangeSource,
+ finallyShortRangeEntry, finallyLongRangeEntry, isFinallyMonitorExceptionPath);
+ }
+ }
+ }
+
+ if (created) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void saveEdge(DirectNode sourcenode,
+ Statement destination,
+ int edgetype,
+ DirectNode finallyShortRangeSource,
+ DirectNode finallyLongRangeSource,
+ Statement finallyShortRangeEntry,
+ Statement finallyLongRangeEntry,
+ boolean isFinallyMonitorExceptionPath) {
+
+ if (edgetype != StatEdge.TYPE_FINALLYEXIT) {
+ listEdges.add(new Edge(sourcenode.id, destination.id, edgetype));
+ }
+
+ if (finallyShortRangeSource != null) {
+
+ boolean isContinueEdge = (edgetype == StatEdge.TYPE_CONTINUE);
+
+ List<String[]> lst = mapShortRangeFinallyPathIds.get(sourcenode.id);
+ if (lst == null) {
+ mapShortRangeFinallyPathIds.put(sourcenode.id, lst = new ArrayList<String[]>());
+ }
+ lst.add(new String[]{finallyShortRangeSource.id, destination.id.toString(), finallyShortRangeEntry.id.toString(),
+ isFinallyMonitorExceptionPath ? "1" : null, isContinueEdge ? "1" : null});
+
+ lst = mapLongRangeFinallyPathIds.get(sourcenode.id);
+ if (lst == null) {
+ mapLongRangeFinallyPathIds.put(sourcenode.id, lst = new ArrayList<String[]>());
+ }
+ lst.add(new String[]{finallyLongRangeSource.id, destination.id.toString(), finallyLongRangeEntry.id.toString(),
+ isContinueEdge ? "1" : null});
+ }
+ }
+
+ private void setEdges() {
+
+ for (Edge edge : listEdges) {
+
+ String sourceid = edge.sourceid;
+ Integer statid = edge.statid;
+
+ DirectNode source = graph.nodes.getWithKey(sourceid);
+
+ DirectNode dest = graph.nodes.getWithKey(mapDestinationNodes.get(statid)[edge.edgetype == StatEdge.TYPE_CONTINUE ? 1 : 0]);
+
+ if (!source.succs.contains(dest)) {
+ source.succs.add(dest);
+ }
+
+ if (!dest.preds.contains(source)) {
+ dest.preds.add(source);
+ }
+
+ if (mapPosIfBranch.containsKey(sourceid) && !statid.equals(mapPosIfBranch.get(sourceid))) {
+ graph.mapNegIfBranch.put(sourceid, dest.id);
+ }
+ }
+
+ for (int i = 0; i < 2; i++) {
+ for (Entry<String, List<String[]>> ent : (i == 0 ? mapShortRangeFinallyPathIds : mapLongRangeFinallyPathIds).entrySet()) {
+
+ List<FinallyPathWrapper> newLst = new ArrayList<FinallyPathWrapper>();
+
+ List<String[]> lst = ent.getValue();
+ for (String[] arr : lst) {
+
+ boolean isContinueEdge = arr[i == 0 ? 4 : 3] != null;
+
+ DirectNode dest = graph.nodes.getWithKey(mapDestinationNodes.get(Integer.parseInt(arr[1]))[isContinueEdge ? 1 : 0]);
+ DirectNode enter = graph.nodes.getWithKey(mapDestinationNodes.get(Integer.parseInt(arr[2]))[0]);
+
+ newLst.add(new FinallyPathWrapper(arr[0], dest.id, enter.id));
+
+ if (i == 0 && arr[3] != null) {
+ graph.mapFinallyMonitorExceptionPathExits.put(ent.getKey(), dest.id);
+ }
+ }
+
+ if (!newLst.isEmpty()) {
+ (i == 0 ? graph.mapShortRangeFinallyPaths : graph.mapLongRangeFinallyPaths).put(ent.getKey(),
+ new ArrayList<FinallyPathWrapper>(
+ new HashSet<FinallyPathWrapper>(newLst)));
+ }
+ }
+ }
+ }
+
+ public Map<Integer, String[]> getMapDestinationNodes() {
+ return mapDestinationNodes;
+ }
+
+ public static class FinallyPathWrapper {
+ public String source;
+ public String destination;
+ public String entry;
+
+ private FinallyPathWrapper(String source, String destination, String entry) {
+ this.source = source;
+ this.destination = destination;
+ this.entry = entry;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (o == null || !(o instanceof FinallyPathWrapper)) return false;
+
+ FinallyPathWrapper fpw = (FinallyPathWrapper)o;
+ return (source + ":" + destination + ":" + entry).equals(fpw.source + ":" + fpw.destination + ":" + fpw.entry);
+ }
+
+ @Override
+ public int hashCode() {
+ return (source + ":" + destination + ":" + entry).hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return source + "->(" + entry + ")->" + destination;
+ }
+ }
+
+
+ private static class StackEntry {
+
+ public CatchAllStatement catchstatement;
+ public boolean state;
+ public int edgetype;
+ public boolean isFinallyExceptionPath;
+
+ public Statement destination;
+ public Statement finallyShortRangeEntry;
+ public Statement finallyLongRangeEntry;
+ public DirectNode finallyShortRangeSource;
+ public DirectNode finallyLongRangeSource;
+
+ public StackEntry(CatchAllStatement catchstatement,
+ boolean state,
+ int edgetype,
+ Statement destination,
+ Statement finallyShortRangeEntry,
+ Statement finallyLongRangeEntry,
+ DirectNode finallyShortRangeSource,
+ DirectNode finallyLongRangeSource,
+ boolean isFinallyExceptionPath) {
+
+ this.catchstatement = catchstatement;
+ this.state = state;
+ this.edgetype = edgetype;
+ this.isFinallyExceptionPath = isFinallyExceptionPath;
+
+ this.destination = destination;
+ this.finallyShortRangeEntry = finallyShortRangeEntry;
+ this.finallyLongRangeEntry = finallyLongRangeEntry;
+ this.finallyShortRangeSource = finallyShortRangeSource;
+ this.finallyLongRangeSource = finallyLongRangeSource;
+ }
+
+ public StackEntry(CatchAllStatement catchstatement, boolean state) {
+ this(catchstatement, state, -1, null, null, null, null, null, false);
+ }
+ }
+
+ private static class Edge {
+ public String sourceid;
+ public Integer statid;
+ public int edgetype;
+
+ public Edge(String sourceid, Integer statid, int edgetype) {
+ this.sourceid = sourceid;
+ this.statid = statid;
+ this.edgetype = edgetype;
+ }
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAConstructorSparseEx.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAConstructorSparseEx.java
new file mode 100644
index 000000000000..dbec7937c77a
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAConstructorSparseEx.java
@@ -0,0 +1,529 @@
+/*
+ * 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.modules.decompiler.sforms;
+
+import org.jetbrains.java.decompiler.code.CodeConstants;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;
+import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper.FinallyPathWrapper;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar;
+import org.jetbrains.java.decompiler.struct.StructMethod;
+import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
+import org.jetbrains.java.decompiler.util.FastSparseSetFactory;
+import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+import org.jetbrains.java.decompiler.util.SFormsFastMapDirect;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map.Entry;
+
+public class SSAConstructorSparseEx {
+
+ // node id, var, version
+ private HashMap<String, SFormsFastMapDirect> inVarVersions = new HashMap<String, SFormsFastMapDirect>();
+
+ // node id, var, version (direct branch)
+ private HashMap<String, SFormsFastMapDirect> outVarVersions = new HashMap<String, SFormsFastMapDirect>();
+
+ // node id, var, version (negative branch)
+ private HashMap<String, SFormsFastMapDirect> outNegVarVersions = new HashMap<String, SFormsFastMapDirect>();
+
+ // node id, var, version
+ private HashMap<String, SFormsFastMapDirect> extraVarVersions = new HashMap<String, SFormsFastMapDirect>();
+
+ // (var, version), version
+ private HashMap<VarVersionPaar, FastSparseSet<Integer>> phi = new HashMap<VarVersionPaar, FastSparseSet<Integer>>();
+
+ // var, version
+ private HashMap<Integer, Integer> lastversion = new HashMap<Integer, Integer>();
+
+ private List<VarVersionPaar> startVars = new ArrayList<VarVersionPaar>();
+
+ // set factory
+ private FastSparseSetFactory<Integer> factory;
+
+ public void splitVariables(RootStatement root, StructMethod mt) {
+
+ FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();
+ DirectGraph dgraph = flatthelper.buildDirectGraph(root);
+
+ // try {
+ // DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr12_my.dot"));
+ // } catch(Exception ex) {ex.printStackTrace();}
+
+ HashSet<Integer> setInit = new HashSet<Integer>();
+ for (int i = 0; i < 64; i++) {
+ setInit.add(i);
+ }
+ factory = new FastSparseSetFactory<Integer>(setInit);
+
+ SFormsFastMapDirect firstmap = createFirstMap(mt);
+ extraVarVersions.put(dgraph.first.id, firstmap);
+
+ setCatchMaps(root, dgraph, flatthelper);
+
+ HashSet<String> updated = new HashSet<String>();
+ do {
+ // System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava());
+ ssaStatements(dgraph, updated);
+ // System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava());
+ }
+ while (!updated.isEmpty());
+ }
+
+ private void ssaStatements(DirectGraph dgraph, HashSet<String> updated) {
+
+ // try {
+ // DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr1_my.dot"));
+ // } catch(Exception ex) {ex.printStackTrace();}
+
+ for (DirectNode node : dgraph.nodes) {
+
+ // if (node.id.endsWith("_inc")) {
+ // System.out.println();
+ //
+ // try {
+ // DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr1_my.dot"));
+ // } catch (Exception ex) {
+ // ex.printStackTrace();
+ // }
+ // }
+
+ updated.remove(node.id);
+ mergeInVarMaps(node, dgraph);
+
+ SFormsFastMapDirect varmap = inVarVersions.get(node.id);
+ varmap = new SFormsFastMapDirect(varmap);
+
+ SFormsFastMapDirect[] varmaparr = new SFormsFastMapDirect[]{varmap, null};
+
+ if (node.exprents != null) {
+ for (Exprent expr : node.exprents) {
+ processExprent(expr, varmaparr);
+ }
+ }
+
+ if (varmaparr[1] == null) {
+ varmaparr[1] = varmaparr[0];
+ }
+
+ boolean this_updated = !mapsEqual(varmaparr[0], outVarVersions.get(node.id))
+ || (outNegVarVersions.containsKey(node.id) && !mapsEqual(varmaparr[1], outNegVarVersions.get(node.id)));
+
+ if (this_updated) {
+ outVarVersions.put(node.id, varmaparr[0]);
+ if (dgraph.mapNegIfBranch.containsKey(node.id)) {
+ outNegVarVersions.put(node.id, varmaparr[1]);
+ }
+
+ for (DirectNode nd : node.succs) {
+ updated.add(nd.id);
+ }
+ }
+ }
+ }
+
+ private void processExprent(Exprent expr, SFormsFastMapDirect[] varmaparr) {
+
+ if (expr == null) {
+ return;
+ }
+
+ VarExprent varassign = null;
+ boolean finished = false;
+
+ switch (expr.type) {
+ case Exprent.EXPRENT_ASSIGNMENT:
+ AssignmentExprent assexpr = (AssignmentExprent)expr;
+ if (assexpr.getCondtype() == AssignmentExprent.CONDITION_NONE) {
+ Exprent dest = assexpr.getLeft();
+ if (dest.type == Exprent.EXPRENT_VAR) {
+ varassign = (VarExprent)dest;
+ }
+ }
+ break;
+ case Exprent.EXPRENT_FUNCTION:
+ FunctionExprent func = (FunctionExprent)expr;
+ switch (func.getFunctype()) {
+ case FunctionExprent.FUNCTION_IIF:
+ processExprent(func.getLstOperands().get(0), varmaparr);
+
+ SFormsFastMapDirect varmapFalse;
+ if (varmaparr[1] == null) {
+ varmapFalse = new SFormsFastMapDirect(varmaparr[0]);
+ }
+ else {
+ varmapFalse = varmaparr[1];
+ varmaparr[1] = null;
+ }
+
+ processExprent(func.getLstOperands().get(1), varmaparr);
+
+ SFormsFastMapDirect[] varmaparrNeg = new SFormsFastMapDirect[]{varmapFalse, null};
+ processExprent(func.getLstOperands().get(2), varmaparrNeg);
+
+ mergeMaps(varmaparr[0], varmaparrNeg[0]);
+ varmaparr[1] = null;
+
+ finished = true;
+ break;
+ case FunctionExprent.FUNCTION_CADD:
+ processExprent(func.getLstOperands().get(0), varmaparr);
+
+ SFormsFastMapDirect[] varmaparrAnd = new SFormsFastMapDirect[]{new SFormsFastMapDirect(varmaparr[0]), null};
+
+ processExprent(func.getLstOperands().get(1), varmaparrAnd);
+
+ // false map
+ varmaparr[1] = mergeMaps(varmaparr[varmaparr[1] == null ? 0 : 1], varmaparrAnd[varmaparrAnd[1] == null ? 0 : 1]);
+ // true map
+ varmaparr[0] = varmaparrAnd[0];
+
+ finished = true;
+ break;
+ case FunctionExprent.FUNCTION_COR:
+ processExprent(func.getLstOperands().get(0), varmaparr);
+
+ SFormsFastMapDirect[] varmaparrOr =
+ new SFormsFastMapDirect[]{new SFormsFastMapDirect(varmaparr[varmaparr[1] == null ? 0 : 1]), null};
+
+ processExprent(func.getLstOperands().get(1), varmaparrOr);
+
+ // false map
+ varmaparr[1] = varmaparrOr[varmaparrOr[1] == null ? 0 : 1];
+ // true map
+ varmaparr[0] = mergeMaps(varmaparr[0], varmaparrOr[0]);
+
+ finished = true;
+ }
+ }
+
+ if (finished) {
+ return;
+ }
+
+ List<Exprent> lst = expr.getAllExprents();
+ lst.remove(varassign);
+
+ for (Exprent ex : lst) {
+ processExprent(ex, varmaparr);
+ }
+
+ SFormsFastMapDirect varmap = varmaparr[0];
+
+ if (varassign != null) {
+
+ Integer varindex = varassign.getIndex();
+
+ if (varassign.getVersion() == 0) {
+ // get next version
+ Integer nextver = getNextFreeVersion(varindex);
+
+ // set version
+ varassign.setVersion(nextver);
+
+ setCurrentVar(varmap, varindex, nextver);
+ }
+ else {
+ setCurrentVar(varmap, varindex, varassign.getVersion());
+ }
+ }
+ else if (expr.type == Exprent.EXPRENT_VAR) {
+
+ VarExprent vardest = (VarExprent)expr;
+ Integer varindex = vardest.getIndex();
+ FastSparseSet<Integer> vers = varmap.get(varindex);
+
+ int cardinality = vers.getCardinality();
+ if (cardinality == 1) { // == 1
+ // set version
+ Integer it = vers.iterator().next();
+ vardest.setVersion(it.intValue());
+ }
+ else if (cardinality == 2) { // size > 1
+ Integer current_vers = vardest.getVersion();
+
+ VarVersionPaar currpaar = new VarVersionPaar(varindex, current_vers);
+ if (current_vers != 0 && phi.containsKey(currpaar)) {
+ setCurrentVar(varmap, varindex, current_vers);
+ // update phi node
+ phi.get(currpaar).union(vers);
+ }
+ else {
+ // increase version
+ Integer nextver = getNextFreeVersion(varindex);
+ // set version
+ vardest.setVersion(nextver);
+
+ setCurrentVar(varmap, varindex, nextver);
+ // create new phi node
+ phi.put(new VarVersionPaar(varindex, nextver), vers);
+ }
+ } // 0 means uninitialized variable, which is impossible
+ }
+ }
+
+ private Integer getNextFreeVersion(Integer var) {
+ Integer nextver = lastversion.get(var);
+ if (nextver == null) {
+ nextver = new Integer(1);
+ }
+ else {
+ nextver = new Integer(nextver.intValue() + 1);
+ }
+ lastversion.put(var, nextver);
+ return nextver;
+ }
+
+ private void mergeInVarMaps(DirectNode node, DirectGraph dgraph) {
+
+ SFormsFastMapDirect mapNew = new SFormsFastMapDirect();
+
+ for (DirectNode pred : node.preds) {
+ SFormsFastMapDirect mapOut = getFilteredOutMap(node.id, pred.id, dgraph, node.id);
+ if (mapNew.isEmpty()) {
+ mapNew = mapOut.getCopy();
+ }
+ else {
+ mergeMaps(mapNew, mapOut);
+ }
+ }
+
+ if (extraVarVersions.containsKey(node.id)) {
+ SFormsFastMapDirect mapExtra = extraVarVersions.get(node.id);
+ if (mapNew.isEmpty()) {
+ mapNew = mapExtra.getCopy();
+ }
+ else {
+ mergeMaps(mapNew, mapExtra);
+ }
+ }
+
+ inVarVersions.put(node.id, mapNew);
+ }
+
+ private SFormsFastMapDirect getFilteredOutMap(String nodeid, String predid, DirectGraph dgraph, String destid) {
+
+ SFormsFastMapDirect mapNew = new SFormsFastMapDirect();
+
+ if (nodeid.equals(dgraph.mapNegIfBranch.get(predid))) {
+ if (outNegVarVersions.containsKey(predid)) {
+ mapNew = outNegVarVersions.get(predid).getCopy();
+ }
+ }
+ else if (outVarVersions.containsKey(predid)) {
+ mapNew = outVarVersions.get(predid).getCopy();
+ }
+
+ boolean isFinallyExit = dgraph.mapShortRangeFinallyPaths.containsKey(predid);
+
+ if (isFinallyExit && !mapNew.isEmpty()) {
+
+ SFormsFastMapDirect mapNewTemp = mapNew.getCopy();
+
+ SFormsFastMapDirect mapTrueSource = new SFormsFastMapDirect();
+
+ String exceptionDest = dgraph.mapFinallyMonitorExceptionPathExits.get(predid);
+ boolean isExceptionMonitorExit = (exceptionDest != null && !nodeid.equals(exceptionDest));
+
+ HashSet<String> setLongPathWrapper = new HashSet<String>();
+ for (FinallyPathWrapper finwraplong : dgraph.mapLongRangeFinallyPaths.get(predid)) {
+ setLongPathWrapper.add(finwraplong.destination + "##" + finwraplong.source);
+ }
+
+ for (FinallyPathWrapper finwrap : dgraph.mapShortRangeFinallyPaths.get(predid)) {
+ SFormsFastMapDirect map;
+
+ boolean recFinally = dgraph.mapShortRangeFinallyPaths.containsKey(finwrap.source);
+
+ if (recFinally) {
+ // recursion
+ map = getFilteredOutMap(finwrap.entry, finwrap.source, dgraph, destid);
+ }
+ else {
+ if (finwrap.entry.equals(dgraph.mapNegIfBranch.get(finwrap.source))) {
+ map = outNegVarVersions.get(finwrap.source);
+ }
+ else {
+ map = outVarVersions.get(finwrap.source);
+ }
+ }
+
+ // false path?
+ boolean isFalsePath = true;
+
+ if (recFinally) {
+ isFalsePath = !finwrap.destination.equals(nodeid);
+ }
+ else {
+ isFalsePath = !setLongPathWrapper.contains(destid + "##" + finwrap.source);
+ }
+
+ if (isFalsePath) {
+ mapNewTemp.complement(map);
+ }
+ else {
+ if (mapTrueSource.isEmpty()) {
+ if (map != null) {
+ mapTrueSource = map.getCopy();
+ }
+ }
+ else {
+ mergeMaps(mapTrueSource, map);
+ }
+ }
+ }
+
+ if (isExceptionMonitorExit) {
+
+ mapNew = mapTrueSource;
+ }
+ else {
+
+ mapNewTemp.union(mapTrueSource);
+
+ SFormsFastMapDirect oldInMap = inVarVersions.get(nodeid);
+ if (oldInMap != null) {
+ mapNewTemp.union(oldInMap);
+ }
+
+ mapNew.intersection(mapNewTemp);
+ }
+ }
+
+ return mapNew;
+ }
+
+ private static SFormsFastMapDirect mergeMaps(SFormsFastMapDirect mapTo, SFormsFastMapDirect map2) {
+
+ if (map2 != null && !map2.isEmpty()) {
+ mapTo.union(map2);
+ }
+
+ return mapTo;
+ }
+
+ private static boolean mapsEqual(SFormsFastMapDirect map1, SFormsFastMapDirect map2) {
+
+ if (map1 == null) {
+ return map2 == null;
+ }
+ else if (map2 == null) {
+ return false;
+ }
+
+ if (map1.size() != map2.size()) {
+ return false;
+ }
+
+ for (Entry<Integer, FastSparseSet<Integer>> ent2 : map2.entryList()) {
+ if (!InterpreterUtil.equalObjects(map1.get(ent2.getKey()), ent2.getValue())) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private void setCurrentVar(SFormsFastMapDirect varmap, Integer var, Integer vers) {
+ FastSparseSet<Integer> set = factory.spawnEmptySet();
+ set.add(vers);
+ varmap.put(var, set);
+ }
+
+ private void setCatchMaps(Statement stat, DirectGraph dgraph, FlattenStatementsHelper flatthelper) {
+
+ SFormsFastMapDirect map;
+
+ switch (stat.type) {
+ case Statement.TYPE_CATCHALL:
+ case Statement.TYPE_TRYCATCH:
+
+ List<VarExprent> lstVars;
+ if (stat.type == Statement.TYPE_CATCHALL) {
+ lstVars = ((CatchAllStatement)stat).getVars();
+ }
+ else {
+ lstVars = ((CatchStatement)stat).getVars();
+ }
+
+ for (int i = 1; i < stat.getStats().size(); i++) {
+ int varindex = lstVars.get(i - 1).getIndex();
+ int version = getNextFreeVersion(varindex); // == 1
+
+ map = new SFormsFastMapDirect();
+ setCurrentVar(map, varindex, version);
+
+ extraVarVersions.put(dgraph.nodes.getWithKey(flatthelper.getMapDestinationNodes().get(stat.getStats().get(i).id)[0]).id, map);
+ startVars.add(new VarVersionPaar(varindex, version));
+ }
+ }
+
+ for (Statement st : stat.getStats()) {
+ setCatchMaps(st, dgraph, flatthelper);
+ }
+ }
+
+ private SFormsFastMapDirect createFirstMap(StructMethod mt) {
+ boolean thisvar = !mt.hasModifier(CodeConstants.ACC_STATIC);
+
+ MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
+
+ int paramcount = md.params.length + (thisvar ? 1 : 0);
+
+ int varindex = 0;
+ SFormsFastMapDirect map = new SFormsFastMapDirect();
+ for (int i = 0; i < paramcount; i++) {
+ int version = getNextFreeVersion(varindex); // == 1
+
+ FastSparseSet<Integer> set = factory.spawnEmptySet();
+ set.add(version);
+ map.put(varindex, set);
+ startVars.add(new VarVersionPaar(varindex, version));
+
+ if (thisvar) {
+ if (i == 0) {
+ varindex++;
+ }
+ else {
+ varindex += md.params[i - 1].stack_size;
+ }
+ }
+ else {
+ varindex += md.params[i].stack_size;
+ }
+ }
+
+ return map;
+ }
+
+ public HashMap<VarVersionPaar, FastSparseSet<Integer>> getPhi() {
+ return phi;
+ }
+
+ public List<VarVersionPaar> getStartVars() {
+ return startVars;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAUConstructorSparseEx.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAUConstructorSparseEx.java
new file mode 100644
index 000000000000..fbd652cd442c
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAUConstructorSparseEx.java
@@ -0,0 +1,844 @@
+/*
+ * 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.modules.decompiler.sforms;
+
+import org.jetbrains.java.decompiler.code.CodeConstants;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
+import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper.FinallyPathWrapper;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.*;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionEdge;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionNode;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionsGraph;
+import org.jetbrains.java.decompiler.struct.StructMethod;
+import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
+import org.jetbrains.java.decompiler.util.FastSparseSetFactory;
+import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+import org.jetbrains.java.decompiler.util.SFormsFastMapDirect;
+import org.jetbrains.java.decompiler.util.VBStyleCollection;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map.Entry;
+
+public class SSAUConstructorSparseEx {
+
+ // node id, var, version
+ private HashMap<String, SFormsFastMapDirect> inVarVersions = new HashMap<String, SFormsFastMapDirect>();
+ //private HashMap<String, HashMap<Integer, FastSet<Integer>>> inVarVersions = new HashMap<String, HashMap<Integer, FastSet<Integer>>>();
+
+ // node id, var, version (direct branch)
+ private HashMap<String, SFormsFastMapDirect> outVarVersions = new HashMap<String, SFormsFastMapDirect>();
+ //private HashMap<String, HashMap<Integer, FastSet<Integer>>> outVarVersions = new HashMap<String, HashMap<Integer, FastSet<Integer>>>();
+
+ // node id, var, version (negative branch)
+ private HashMap<String, SFormsFastMapDirect> outNegVarVersions = new HashMap<String, SFormsFastMapDirect>();
+ //private HashMap<String, HashMap<Integer, FastSet<Integer>>> outNegVarVersions = new HashMap<String, HashMap<Integer, FastSet<Integer>>>();
+
+ // node id, var, version
+ private HashMap<String, SFormsFastMapDirect> extraVarVersions = new HashMap<String, SFormsFastMapDirect>();
+ //private HashMap<String, HashMap<Integer, FastSet<Integer>>> extraVarVersions = new HashMap<String, HashMap<Integer, FastSet<Integer>>>();
+
+ // (var, version), version
+ private HashMap<VarVersionPaar, HashSet<Integer>> phi = new HashMap<VarVersionPaar, HashSet<Integer>>();
+
+ // var, version
+ private HashMap<Integer, Integer> lastversion = new HashMap<Integer, Integer>();
+
+ // version, protected ranges (catch, finally)
+ private HashMap<VarVersionPaar, Integer> mapVersionFirstRange = new HashMap<VarVersionPaar, Integer>();
+
+ // version, version
+ private HashMap<VarVersionPaar, VarVersionPaar> phantomppnodes = new HashMap<VarVersionPaar, VarVersionPaar>(); // ++ and --
+
+ // node.id, version, version
+ private HashMap<String, HashMap<VarVersionPaar, VarVersionPaar>> phantomexitnodes =
+ new HashMap<String, HashMap<VarVersionPaar, VarVersionPaar>>(); // finally exits
+
+ // versions memory dependencies
+ private VarVersionsGraph ssuversions = new VarVersionsGraph();
+
+ // field access vars (exprent id, var id)
+ private HashMap<Integer, Integer> mapFieldVars = new HashMap<Integer, Integer>();
+
+ // field access counter
+ private int fieldvarcounter = -1;
+
+ // set factory
+ private FastSparseSetFactory<Integer> factory;
+
+ public void splitVariables(RootStatement root, StructMethod mt) {
+
+ FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();
+ DirectGraph dgraph = flatthelper.buildDirectGraph(root);
+
+ HashSet<Integer> setInit = new HashSet<Integer>();
+ for (int i = 0; i < 64; i++) {
+ setInit.add(i);
+ }
+ factory = new FastSparseSetFactory<Integer>(setInit);
+
+ extraVarVersions.put(dgraph.first.id, createFirstMap(mt, root));
+
+ setCatchMaps(root, dgraph, flatthelper);
+
+ // try {
+ // DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr12_my.dot"));
+ // } catch(Exception ex) {ex.printStackTrace();}
+
+ HashSet<String> updated = new HashSet<String>();
+ do {
+ // System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava());
+ ssaStatements(dgraph, updated, false);
+ // System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava());
+ }
+ while (!updated.isEmpty());
+
+
+ ssaStatements(dgraph, updated, true);
+
+ ssuversions.initDominators();
+ }
+
+ private void ssaStatements(DirectGraph dgraph, HashSet<String> updated, boolean calcLiveVars) {
+
+ for (DirectNode node : dgraph.nodes) {
+
+ updated.remove(node.id);
+ mergeInVarMaps(node, dgraph);
+
+ SFormsFastMapDirect varmap = new SFormsFastMapDirect(inVarVersions.get(node.id));
+
+ SFormsFastMapDirect[] varmaparr = new SFormsFastMapDirect[]{varmap, null};
+
+ if (node.exprents != null) {
+ for (Exprent expr : node.exprents) {
+ processExprent(expr, varmaparr, node.statement, calcLiveVars);
+ }
+ }
+
+ if (varmaparr[1] == null) {
+ varmaparr[1] = varmaparr[0];
+ }
+
+ // quick solution: 'dummy' field variables should not cross basic block borders (otherwise problems e.g. with finally loops - usage without assignment in a loop)
+ // For the full solution consider adding a dummy assignment at the entry point of the method
+ boolean allow_field_propagation = node.succs.isEmpty() || (node.succs.size() == 1 && node.succs.get(0).preds.size() == 1);
+
+ if (!allow_field_propagation && varmaparr[0] != null) {
+ varmaparr[0].removeAllFields();
+ varmaparr[1].removeAllFields();
+ }
+
+ boolean this_updated = !mapsEqual(varmaparr[0], outVarVersions.get(node.id))
+ || (outNegVarVersions.containsKey(node.id) && !mapsEqual(varmaparr[1], outNegVarVersions.get(node.id)));
+
+ if (this_updated) {
+
+ outVarVersions.put(node.id, varmaparr[0]);
+ if (dgraph.mapNegIfBranch.containsKey(node.id)) {
+ outNegVarVersions.put(node.id, varmaparr[1]);
+ }
+
+ for (DirectNode nd : node.succs) {
+ updated.add(nd.id);
+ }
+ }
+ }
+ }
+
+
+ private void processExprent(Exprent expr, SFormsFastMapDirect[] varmaparr, Statement stat, boolean calcLiveVars) {
+
+ if (expr == null) {
+ return;
+ }
+
+
+ VarExprent varassign = null;
+ boolean finished = false;
+
+ switch (expr.type) {
+ case Exprent.EXPRENT_ASSIGNMENT:
+ AssignmentExprent assexpr = (AssignmentExprent)expr;
+ if (assexpr.getCondtype() == AssignmentExprent.CONDITION_NONE) {
+ Exprent dest = assexpr.getLeft();
+ if (dest.type == Exprent.EXPRENT_VAR) {
+ varassign = (VarExprent)dest;
+ }
+ }
+ break;
+ case Exprent.EXPRENT_FUNCTION:
+ FunctionExprent func = (FunctionExprent)expr;
+ switch (func.getFunctype()) {
+ case FunctionExprent.FUNCTION_IIF:
+ processExprent(func.getLstOperands().get(0), varmaparr, stat, calcLiveVars);
+
+ SFormsFastMapDirect varmapFalse;
+ if (varmaparr[1] == null) {
+ varmapFalse = new SFormsFastMapDirect(varmaparr[0]);
+ }
+ else {
+ varmapFalse = varmaparr[1];
+ varmaparr[1] = null;
+ }
+
+ processExprent(func.getLstOperands().get(1), varmaparr, stat, calcLiveVars);
+
+ SFormsFastMapDirect[] varmaparrNeg = new SFormsFastMapDirect[]{varmapFalse, null};
+ processExprent(func.getLstOperands().get(2), varmaparrNeg, stat, calcLiveVars);
+
+ mergeMaps(varmaparr[0], varmaparrNeg[0]);
+ varmaparr[1] = null;
+
+ finished = true;
+ break;
+ case FunctionExprent.FUNCTION_CADD:
+ processExprent(func.getLstOperands().get(0), varmaparr, stat, calcLiveVars);
+
+ SFormsFastMapDirect[] varmaparrAnd = new SFormsFastMapDirect[]{new SFormsFastMapDirect(varmaparr[0]), null};
+
+ processExprent(func.getLstOperands().get(1), varmaparrAnd, stat, calcLiveVars);
+
+ // false map
+ varmaparr[1] = mergeMaps(varmaparr[varmaparr[1] == null ? 0 : 1], varmaparrAnd[varmaparrAnd[1] == null ? 0 : 1]);
+ // true map
+ varmaparr[0] = varmaparrAnd[0];
+
+ finished = true;
+ break;
+ case FunctionExprent.FUNCTION_COR:
+ processExprent(func.getLstOperands().get(0), varmaparr, stat, calcLiveVars);
+
+ SFormsFastMapDirect[] varmaparrOr =
+ new SFormsFastMapDirect[]{new SFormsFastMapDirect(varmaparr[varmaparr[1] == null ? 0 : 1]), null};
+
+ processExprent(func.getLstOperands().get(1), varmaparrOr, stat, calcLiveVars);
+
+ // false map
+ varmaparr[1] = varmaparrOr[varmaparrOr[1] == null ? 0 : 1];
+ // true map
+ varmaparr[0] = mergeMaps(varmaparr[0], varmaparrOr[0]);
+
+ finished = true;
+ }
+ }
+
+ if (!finished) {
+ List<Exprent> lst = expr.getAllExprents();
+ lst.remove(varassign);
+
+ for (Exprent ex : lst) {
+ processExprent(ex, varmaparr, stat, calcLiveVars);
+ }
+ }
+
+
+ SFormsFastMapDirect varmap = varmaparr[0];
+
+ // field access
+ if (expr.type == Exprent.EXPRENT_FIELD) {
+
+ int index;
+ if (mapFieldVars.containsKey(expr.id)) {
+ index = mapFieldVars.get(expr.id);
+ }
+ else {
+ index = fieldvarcounter--;
+ mapFieldVars.put(expr.id, index);
+
+ // ssu graph
+ ssuversions.createNode(new VarVersionPaar(index, 1));
+ }
+
+ setCurrentVar(varmap, index, 1);
+ }
+ else if (expr.type == Exprent.EXPRENT_INVOCATION ||
+ (expr.type == Exprent.EXPRENT_ASSIGNMENT && ((AssignmentExprent)expr).getLeft().type == Exprent.EXPRENT_FIELD) ||
+ (expr.type == Exprent.EXPRENT_NEW && ((NewExprent)expr).getNewtype().type == CodeConstants.TYPE_OBJECT) ||
+ expr.type == Exprent.EXPRENT_FUNCTION) {
+
+ boolean ismmpp = true;
+
+ if (expr.type == Exprent.EXPRENT_FUNCTION) {
+
+ ismmpp = false;
+
+ FunctionExprent fexpr = (FunctionExprent)expr;
+ if (fexpr.getFunctype() >= FunctionExprent.FUNCTION_IMM && fexpr.getFunctype() <= FunctionExprent.FUNCTION_PPI) {
+ if (fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) {
+ ismmpp = true;
+ }
+ }
+ }
+
+ if (ismmpp) {
+ varmap.removeAllFields();
+ }
+ }
+
+
+ if (varassign != null) {
+
+ Integer varindex = varassign.getIndex();
+
+ if (varassign.getVersion() == 0) {
+ // get next version
+ Integer nextver = getNextFreeVersion(varindex, stat);
+
+ // set version
+ varassign.setVersion(nextver);
+
+ // ssu graph
+ ssuversions.createNode(new VarVersionPaar(varindex, nextver));
+
+ setCurrentVar(varmap, varindex, nextver);
+ }
+ else {
+ if (calcLiveVars) {
+ varMapToGraph(new VarVersionPaar(varindex.intValue(), varassign.getVersion()), varmap);
+ }
+ setCurrentVar(varmap, varindex, varassign.getVersion());
+ }
+ }
+ else if (expr.type == Exprent.EXPRENT_FUNCTION) { // MM or PP function
+ FunctionExprent func = (FunctionExprent)expr;
+
+ switch (func.getFunctype()) {
+ case FunctionExprent.FUNCTION_IMM:
+ case FunctionExprent.FUNCTION_MMI:
+ case FunctionExprent.FUNCTION_IPP:
+ case FunctionExprent.FUNCTION_PPI:
+
+ if (func.getLstOperands().get(0).type == Exprent.EXPRENT_VAR) {
+ VarExprent var = (VarExprent)func.getLstOperands().get(0);
+ Integer varindex = var.getIndex();
+ VarVersionPaar varpaar = new VarVersionPaar(varindex.intValue(), var.getVersion());
+
+ // ssu graph
+ VarVersionPaar phantomver = phantomppnodes.get(varpaar);
+ if (phantomver == null) {
+ // get next version
+ Integer nextver = getNextFreeVersion(varindex, null);
+ phantomver = new VarVersionPaar(varindex, nextver);
+ //ssuversions.createOrGetNode(phantomver);
+ ssuversions.createNode(phantomver);
+
+ VarVersionNode vernode = ssuversions.nodes.getWithKey(varpaar);
+
+ FastSparseSet<Integer> vers = factory.spawnEmptySet();
+ if (vernode.preds.size() == 1) {
+ vers.add(vernode.preds.iterator().next().source.version);
+ }
+ else {
+ for (VarVersionEdge edge : vernode.preds) {
+ vers.add(edge.source.preds.iterator().next().source.version);
+ }
+ }
+ vers.add(nextver);
+ createOrUpdatePhiNode(varpaar, vers, stat);
+ phantomppnodes.put(varpaar, phantomver);
+ }
+ if (calcLiveVars) {
+ varMapToGraph(varpaar, varmap);
+ }
+ setCurrentVar(varmap, varindex.intValue(), var.getVersion());
+ }
+ }
+ }
+ else if (expr.type == Exprent.EXPRENT_VAR) {
+
+ VarExprent vardest = (VarExprent)expr;
+
+ Integer varindex = vardest.getIndex();
+ Integer current_vers = vardest.getVersion();
+
+ FastSparseSet<Integer> vers = varmap.get(varindex);
+
+ int cardinality = vers.getCardinality();
+ if (cardinality == 1) { // size == 1
+ if (current_vers.intValue() != 0) {
+ if (calcLiveVars) {
+ varMapToGraph(new VarVersionPaar(varindex, current_vers), varmap);
+ }
+ setCurrentVar(varmap, varindex, current_vers);
+ }
+ else {
+ // split last version
+ Integer usever = getNextFreeVersion(varindex, stat);
+
+ // set version
+ vardest.setVersion(usever);
+ setCurrentVar(varmap, varindex, usever);
+
+ // ssu graph
+ Integer lastver = vers.iterator().next();
+ VarVersionNode prenode = ssuversions.nodes.getWithKey(new VarVersionPaar(varindex, lastver));
+ VarVersionNode usenode = ssuversions.createNode(new VarVersionPaar(varindex, usever));
+ VarVersionEdge edge = new VarVersionEdge(VarVersionEdge.EDGE_GENERAL, prenode, usenode);
+ prenode.addSuccessor(edge);
+ usenode.addPredecessor(edge);
+ }
+ }
+ else if (cardinality == 2) { // size > 1
+
+ if (current_vers.intValue() != 0) {
+ if (calcLiveVars) {
+ varMapToGraph(new VarVersionPaar(varindex, current_vers), varmap);
+ }
+ setCurrentVar(varmap, varindex, current_vers);
+ }
+ else {
+ // split version
+ Integer usever = getNextFreeVersion(varindex, stat);
+ // set version
+ vardest.setVersion(usever);
+
+ // ssu node
+ ssuversions.createNode(new VarVersionPaar(varindex, usever));
+
+ setCurrentVar(varmap, varindex, usever);
+
+ current_vers = usever;
+ }
+
+ createOrUpdatePhiNode(new VarVersionPaar(varindex, current_vers), vers, stat);
+ } // vers.size() == 0 means uninitialized variable, which is impossible
+ }
+ }
+
+ private void createOrUpdatePhiNode(VarVersionPaar phivar, FastSparseSet<Integer> vers, Statement stat) {
+
+ FastSparseSet<Integer> versCopy = vers.getCopy();
+ HashSet<Integer> phiVers = new HashSet<Integer>();
+
+ // take into account the corresponding mm/pp node if existing
+ int ppvers = phantomppnodes.containsKey(phivar) ? phantomppnodes.get(phivar).version : -1;
+
+ // ssu graph
+ VarVersionNode phinode = ssuversions.nodes.getWithKey(phivar);
+ List<VarVersionEdge> lstPreds = new ArrayList<VarVersionEdge>(phinode.preds);
+ if (lstPreds.size() == 1) {
+ // not yet a phi node
+ VarVersionEdge edge = lstPreds.get(0);
+ edge.source.removeSuccessor(edge);
+ phinode.removePredecessor(edge);
+ }
+ else {
+ for (VarVersionEdge edge : lstPreds) {
+ int verssrc = edge.source.preds.iterator().next().source.version;
+ if (!vers.contains(verssrc) && verssrc != ppvers) {
+ edge.source.removeSuccessor(edge);
+ phinode.removePredecessor(edge);
+ }
+ else {
+ versCopy.remove(verssrc);
+ phiVers.add(verssrc);
+ }
+ }
+ }
+
+ List<VarVersionNode> colnodes = new ArrayList<VarVersionNode>();
+ List<VarVersionPaar> colpaars = new ArrayList<VarVersionPaar>();
+
+ for (Integer ver : versCopy) {
+
+ VarVersionNode prenode = ssuversions.nodes.getWithKey(new VarVersionPaar(phivar.var, ver.intValue()));
+
+ Integer tempver = getNextFreeVersion(phivar.var, stat);
+
+ VarVersionNode tempnode = new VarVersionNode(phivar.var, tempver.intValue());
+
+ colnodes.add(tempnode);
+ colpaars.add(new VarVersionPaar(phivar.var, tempver.intValue()));
+
+ VarVersionEdge edge = new VarVersionEdge(VarVersionEdge.EDGE_GENERAL, prenode, tempnode);
+
+ prenode.addSuccessor(edge);
+ tempnode.addPredecessor(edge);
+
+
+ edge = new VarVersionEdge(VarVersionEdge.EDGE_GENERAL, tempnode, phinode);
+ tempnode.addSuccessor(edge);
+ phinode.addPredecessor(edge);
+
+ phiVers.add(tempver);
+ }
+
+ ssuversions.addNodes(colnodes, colpaars);
+
+ // update phi node
+ phi.put(phivar, phiVers);
+ }
+
+ private void varMapToGraph(VarVersionPaar varpaar, SFormsFastMapDirect varmap) {
+
+ VBStyleCollection<VarVersionNode, VarVersionPaar> nodes = ssuversions.nodes;
+
+ VarVersionNode node = nodes.getWithKey(varpaar);
+
+ node.live = new SFormsFastMapDirect(varmap);
+ }
+
+ private Integer getNextFreeVersion(Integer var, Statement stat) {
+
+ Integer nextver = lastversion.get(var);
+
+ if (nextver == null) {
+ nextver = new Integer(1);
+ }
+ else {
+ nextver = new Integer(nextver.intValue() + 1);
+ }
+ lastversion.put(var, nextver);
+
+ // save the first protected range, containing current statement
+ if (stat != null) { // null iff phantom version
+ Integer firstRangeId = getFirstProtectedRange(stat);
+ if (firstRangeId != null) {
+ mapVersionFirstRange.put(new VarVersionPaar(var, nextver), firstRangeId);
+ }
+ }
+
+ return nextver;
+ }
+
+ private void mergeInVarMaps(DirectNode node, DirectGraph dgraph) {
+
+
+ SFormsFastMapDirect mapNew = new SFormsFastMapDirect();
+
+ for (DirectNode pred : node.preds) {
+ SFormsFastMapDirect mapOut = getFilteredOutMap(node.id, pred.id, dgraph, node.id);
+ if (mapNew.isEmpty()) {
+ mapNew = mapOut.getCopy();
+ }
+ else {
+ mergeMaps(mapNew, mapOut);
+ }
+ }
+
+ if (extraVarVersions.containsKey(node.id)) {
+ SFormsFastMapDirect mapExtra = extraVarVersions.get(node.id);
+ if (mapNew.isEmpty()) {
+ mapNew = mapExtra.getCopy();
+ }
+ else {
+ mergeMaps(mapNew, mapExtra);
+ }
+ }
+
+ inVarVersions.put(node.id, mapNew);
+ }
+
+ private SFormsFastMapDirect getFilteredOutMap(String nodeid, String predid, DirectGraph dgraph, String destid) {
+
+ SFormsFastMapDirect mapNew = new SFormsFastMapDirect();
+
+ boolean isFinallyExit = dgraph.mapShortRangeFinallyPaths.containsKey(predid);
+
+ if (nodeid.equals(dgraph.mapNegIfBranch.get(predid))) {
+ if (outNegVarVersions.containsKey(predid)) {
+ mapNew = outNegVarVersions.get(predid).getCopy();
+ }
+ }
+ else if (outVarVersions.containsKey(predid)) {
+ mapNew = outVarVersions.get(predid).getCopy();
+ }
+
+ if (isFinallyExit) {
+
+ SFormsFastMapDirect mapNewTemp = mapNew.getCopy();
+
+ SFormsFastMapDirect mapTrueSource = new SFormsFastMapDirect();
+
+ String exceptionDest = dgraph.mapFinallyMonitorExceptionPathExits.get(predid);
+ boolean isExceptionMonitorExit = (exceptionDest != null && !nodeid.equals(exceptionDest));
+
+ HashSet<String> setLongPathWrapper = new HashSet<String>();
+ for (List<FinallyPathWrapper> lstwrapper : dgraph.mapLongRangeFinallyPaths.values()) {
+ for (FinallyPathWrapper finwraplong : lstwrapper) {
+ setLongPathWrapper.add(finwraplong.destination + "##" + finwraplong.source);
+ }
+ }
+
+ for (FinallyPathWrapper finwrap : dgraph.mapShortRangeFinallyPaths.get(predid)) {
+ SFormsFastMapDirect map;
+
+ boolean recFinally = dgraph.mapShortRangeFinallyPaths.containsKey(finwrap.source);
+
+ if (recFinally) {
+ // recursion
+ map = getFilteredOutMap(finwrap.entry, finwrap.source, dgraph, destid);
+ }
+ else {
+ if (finwrap.entry.equals(dgraph.mapNegIfBranch.get(finwrap.source))) {
+ map = outNegVarVersions.get(finwrap.source);
+ }
+ else {
+ map = outVarVersions.get(finwrap.source);
+ }
+ }
+
+ // false path?
+ boolean isFalsePath = true;
+
+ if (recFinally) {
+ isFalsePath = !finwrap.destination.equals(nodeid);
+ }
+ else {
+ isFalsePath = !setLongPathWrapper.contains(destid + "##" + finwrap.source);
+ }
+
+ if (isFalsePath) {
+ mapNewTemp.complement(map);
+ }
+ else {
+ if (mapTrueSource.isEmpty()) {
+ if (map != null) {
+ mapTrueSource = map.getCopy();
+ }
+ }
+ else {
+ mergeMaps(mapTrueSource, map);
+ }
+ }
+ }
+
+ if (isExceptionMonitorExit) {
+
+ mapNew = mapTrueSource;
+ }
+ else {
+
+ mapNewTemp.union(mapTrueSource);
+ mapNew.intersection(mapNewTemp);
+
+ if (!mapTrueSource.isEmpty() && !mapNew.isEmpty()) { // FIXME: what for??
+
+ // replace phi versions with corresponding phantom ones
+ HashMap<VarVersionPaar, VarVersionPaar> mapPhantom = phantomexitnodes.get(predid);
+ if (mapPhantom == null) {
+ mapPhantom = new HashMap<VarVersionPaar, VarVersionPaar>();
+ }
+
+ SFormsFastMapDirect mapExitVar = mapNew.getCopy();
+ mapExitVar.complement(mapTrueSource);
+
+ for (Entry<Integer, FastSparseSet<Integer>> ent : mapExitVar.entryList()) {
+ for (Integer version : ent.getValue()) {
+
+ Integer varindex = ent.getKey();
+ VarVersionPaar exitvar = new VarVersionPaar(varindex, version);
+ FastSparseSet<Integer> newSet = mapNew.get(varindex);
+
+ // remove the actual exit version
+ newSet.remove(version);
+
+ // get or create phantom version
+ VarVersionPaar phantomvar = mapPhantom.get(exitvar);
+ if (phantomvar == null) {
+ Integer newversion = getNextFreeVersion(exitvar.var, null);
+ phantomvar = new VarVersionPaar(exitvar.var, newversion.intValue());
+
+ VarVersionNode exitnode = ssuversions.nodes.getWithKey(exitvar);
+ VarVersionNode phantomnode = ssuversions.createNode(phantomvar);
+ phantomnode.flags |= VarVersionNode.FLAG_PHANTOM_FINEXIT;
+
+ VarVersionEdge edge = new VarVersionEdge(VarVersionEdge.EDGE_PHANTOM, exitnode, phantomnode);
+ exitnode.addSuccessor(edge);
+ phantomnode.addPredecessor(edge);
+
+ mapPhantom.put(exitvar, phantomvar);
+ }
+
+ // add phantom version
+ newSet.add(phantomvar.version);
+ }
+ }
+
+ if (!mapPhantom.isEmpty()) {
+ phantomexitnodes.put(predid, mapPhantom);
+ }
+ }
+ }
+ }
+
+ return mapNew;
+ }
+
+ private static SFormsFastMapDirect mergeMaps(SFormsFastMapDirect mapTo, SFormsFastMapDirect map2) {
+
+ if (map2 != null && !map2.isEmpty()) {
+ mapTo.union(map2);
+ }
+
+ return mapTo;
+ }
+
+ private static boolean mapsEqual(SFormsFastMapDirect map1, SFormsFastMapDirect map2) {
+
+ if (map1 == null) {
+ return map2 == null;
+ }
+ else if (map2 == null) {
+ return false;
+ }
+
+ if (map1.size() != map2.size()) {
+ return false;
+ }
+
+ for (Entry<Integer, FastSparseSet<Integer>> ent2 : map2.entryList()) {
+ if (!InterpreterUtil.equalObjects(map1.get(ent2.getKey()), ent2.getValue())) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+ private void setCurrentVar(SFormsFastMapDirect varmap, Integer var, Integer vers) {
+ FastSparseSet<Integer> set = factory.spawnEmptySet();
+ set.add(vers);
+ varmap.put(var, set);
+ }
+
+ private void setCatchMaps(Statement stat, DirectGraph dgraph, FlattenStatementsHelper flatthelper) {
+
+ SFormsFastMapDirect map;
+
+ switch (stat.type) {
+ case Statement.TYPE_CATCHALL:
+ case Statement.TYPE_TRYCATCH:
+
+ List<VarExprent> lstVars;
+ if (stat.type == Statement.TYPE_CATCHALL) {
+ lstVars = ((CatchAllStatement)stat).getVars();
+ }
+ else {
+ lstVars = ((CatchStatement)stat).getVars();
+ }
+
+ for (int i = 1; i < stat.getStats().size(); i++) {
+ int varindex = lstVars.get(i - 1).getIndex();
+ int version = getNextFreeVersion(varindex, stat); // == 1
+
+ map = new SFormsFastMapDirect();
+ setCurrentVar(map, varindex, version);
+
+ extraVarVersions.put(dgraph.nodes.getWithKey(flatthelper.getMapDestinationNodes().get(stat.getStats().get(i).id)[0]).id, map);
+ //ssuversions.createOrGetNode(new VarVersionPaar(varindex, version));
+ ssuversions.createNode(new VarVersionPaar(varindex, version));
+ }
+ }
+
+ for (Statement st : stat.getStats()) {
+ setCatchMaps(st, dgraph, flatthelper);
+ }
+ }
+
+ private SFormsFastMapDirect createFirstMap(StructMethod mt, RootStatement root) {
+ boolean thisvar = !mt.hasModifier(CodeConstants.ACC_STATIC);
+
+ MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
+
+ int paramcount = md.params.length + (thisvar ? 1 : 0);
+
+ int varindex = 0;
+ SFormsFastMapDirect map = new SFormsFastMapDirect();
+ for (int i = 0; i < paramcount; i++) {
+ int version = getNextFreeVersion(varindex, root); // == 1
+
+ FastSparseSet<Integer> set = factory.spawnEmptySet();
+ set.add(version);
+ map.put(varindex, set);
+ ssuversions.createNode(new VarVersionPaar(varindex, version));
+
+ if (thisvar) {
+ if (i == 0) {
+ varindex++;
+ }
+ else {
+ varindex += md.params[i - 1].stack_size;
+ }
+ }
+ else {
+ varindex += md.params[i].stack_size;
+ }
+ }
+
+ return map;
+ }
+
+ private static Integer getFirstProtectedRange(Statement stat) {
+
+ while (true) {
+ Statement parent = stat.getParent();
+
+ if (parent == null) {
+ break;
+ }
+
+ if (parent.type == Statement.TYPE_CATCHALL ||
+ parent.type == Statement.TYPE_TRYCATCH) {
+ if (parent.getFirst() == stat) {
+ return parent.id;
+ }
+ }
+ else if (parent.type == Statement.TYPE_SYNCRONIZED) {
+ if (((SynchronizedStatement)parent).getBody() == stat) {
+ return parent.id;
+ }
+ }
+
+ stat = parent;
+ }
+
+ return null;
+ }
+
+ public HashMap<VarVersionPaar, HashSet<Integer>> getPhi() {
+ return phi;
+ }
+
+ public VarVersionsGraph getSsuversions() {
+ return ssuversions;
+ }
+
+ public SFormsFastMapDirect getLiveVarVersionsMap(VarVersionPaar varpaar) {
+
+
+ VarVersionNode node = ssuversions.nodes.getWithKey(varpaar);
+ if (node != null) {
+ return node.live;
+ }
+
+ return null;
+ }
+
+ public HashMap<VarVersionPaar, Integer> getMapVersionFirstRange() {
+ return mapVersionFirstRange;
+ }
+
+ public HashMap<Integer, Integer> getMapFieldVars() {
+ return mapFieldVars;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java
new file mode 100644
index 000000000000..91c1a4e23c84
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java
@@ -0,0 +1,96 @@
+/*
+ * 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.modules.decompiler.stats;
+
+import org.jetbrains.java.decompiler.code.CodeConstants;
+import org.jetbrains.java.decompiler.code.Instruction;
+import org.jetbrains.java.decompiler.code.SimpleInstructionSequence;
+import org.jetbrains.java.decompiler.code.cfg.BasicBlock;
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
+import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
+
+public class BasicBlockStatement extends Statement {
+
+ // *****************************************************************************
+ // private fields
+ // *****************************************************************************
+
+ private BasicBlock block;
+
+ // *****************************************************************************
+ // constructors
+ // *****************************************************************************
+
+ public BasicBlockStatement(BasicBlock block) {
+
+ type = Statement.TYPE_BASICBLOCK;
+
+ this.block = block;
+
+ id = block.id;
+ CounterContainer coun = DecompilerContext.getCounterContainer();
+ if (id >= coun.getCounter(CounterContainer.STATEMENT_COUNTER)) {
+ coun.setCounter(CounterContainer.STATEMENT_COUNTER, id + 1);
+ }
+
+ Instruction instr = block.getLastInstruction();
+ if (instr != null) {
+ if (instr.group == CodeConstants.GROUP_JUMP && instr.opcode != CodeConstants.opc_goto) {
+ lastBasicType = LASTBASICTYPE_IF;
+ }
+ else if (instr.group == CodeConstants.GROUP_SWITCH) {
+ lastBasicType = LASTBASICTYPE_SWITCH;
+ }
+ }
+
+ // monitorenter and monitorexits
+ buildMonitorFlags();
+ }
+
+ // *****************************************************************************
+ // public methods
+ // *****************************************************************************
+
+ public String toJava(int indent) {
+ return ExprProcessor.listToJava(varDefinitions, indent) +
+ ExprProcessor.listToJava(exprents, indent);
+ }
+
+ public Statement getSimpleCopy() {
+
+ BasicBlock newblock = new BasicBlock(
+ DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER));
+
+ SimpleInstructionSequence seq = new SimpleInstructionSequence();
+ for (int i = 0; i < block.getSeq().length(); i++) {
+ seq.addInstruction(block.getSeq().getInstr(i).clone(), -1);
+ }
+
+ newblock.setSeq(seq);
+
+ return new BasicBlockStatement(newblock);
+ }
+
+
+ // *****************************************************************************
+ // getter and setter methods
+ // *****************************************************************************
+
+ public BasicBlock getBlock() {
+ return block;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java
new file mode 100644
index 000000000000..15af3c604a2e
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java
@@ -0,0 +1,233 @@
+/*
+ * 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.modules.decompiler.stats;
+
+import org.jetbrains.java.decompiler.code.CodeConstants;
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
+import org.jetbrains.java.decompiler.modules.decompiler.DecHelper;
+import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
+import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+public class CatchAllStatement extends Statement {
+
+ private Statement handler;
+
+ private boolean isFinally;
+
+ private VarExprent monitor;
+
+ private List<VarExprent> vars = new ArrayList<VarExprent>();
+
+ // *****************************************************************************
+ // constructors
+ // *****************************************************************************
+
+ private CatchAllStatement() {
+ type = Statement.TYPE_CATCHALL;
+ }
+
+ private CatchAllStatement(Statement head, Statement handler) {
+
+ this();
+
+ first = head;
+ stats.addWithKey(head, head.id);
+
+ this.handler = handler;
+ stats.addWithKey(handler, handler.id);
+
+ List<StatEdge> lstSuccs = head.getSuccessorEdges(STATEDGE_DIRECT_ALL);
+ if (!lstSuccs.isEmpty()) {
+ StatEdge edge = lstSuccs.get(0);
+ if (edge.getType() == StatEdge.TYPE_REGULAR) {
+ post = edge.getDestination();
+ }
+ }
+
+ vars.add(new VarExprent(DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER),
+ new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Throwable"),
+ (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR)));
+ }
+
+
+ // *****************************************************************************
+ // public methods
+ // *****************************************************************************
+
+ public static Statement isHead(Statement head) {
+
+ if (head.getLastBasicType() != Statement.LASTBASICTYPE_GENERAL) {
+ return null;
+ }
+
+ HashSet<Statement> setHandlers = DecHelper.getUniquePredExceptions(head);
+
+ if (setHandlers.size() != 1) {
+ return null;
+ }
+
+ for (StatEdge edge : head.getSuccessorEdges(StatEdge.TYPE_EXCEPTION)) {
+ Statement exc = edge.getDestination();
+
+ if (edge.getExceptions() == null && setHandlers.contains(exc) && exc.getLastBasicType() == LASTBASICTYPE_GENERAL) {
+ List<StatEdge> lstSuccs = exc.getSuccessorEdges(STATEDGE_DIRECT_ALL);
+ if (lstSuccs.isEmpty() || lstSuccs.get(0).getType() != StatEdge.TYPE_REGULAR) {
+
+ if (head.isMonitorEnter() || exc.isMonitorEnter()) {
+ return null;
+ }
+
+ if (DecHelper.checkStatementExceptions(Arrays.asList(head, exc))) {
+ return new CatchAllStatement(head, exc);
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public String toJava(int indent) {
+ String indstr = InterpreterUtil.getIndentString(indent);
+ String indstr1 = null;
+
+ String new_line_separator = DecompilerContext.getNewLineSeparator();
+
+ StringBuilder buf = new StringBuilder();
+
+ buf.append(ExprProcessor.listToJava(varDefinitions, indent));
+
+ boolean labeled = isLabeled();
+ if (labeled) {
+ buf.append(indstr).append("label").append(this.id).append(":").append(new_line_separator);
+ }
+
+ List<StatEdge> lstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL);
+ if (first.type == TYPE_TRYCATCH && first.varDefinitions.isEmpty() && isFinally &&
+ !labeled && !first.isLabeled() && (lstSuccs.isEmpty() || !lstSuccs.get(0).explicit)) {
+ String content = ExprProcessor.jmpWrapper(first, indent, true);
+ content = content.substring(0, content.length() - new_line_separator.length());
+
+ buf.append(content);
+ }
+ else {
+ buf.append(indstr).append("try {").append(new_line_separator);
+ buf.append(ExprProcessor.jmpWrapper(first, indent + 1, true));
+ buf.append(indstr).append("}");
+ }
+
+ buf.append(isFinally ? " finally" :
+ " catch (" + vars.get(0).toJava(indent) + ")").append(" {").append(new_line_separator);
+
+ if (monitor != null) {
+ indstr1 = InterpreterUtil.getIndentString(indent + 1);
+ buf.append(indstr1).append("if(").append(monitor.toJava(indent)).append(") {").append(new_line_separator);
+ }
+
+ buf.append(ExprProcessor.jmpWrapper(handler, indent + 1 + (monitor != null ? 1 : 0), true));
+
+ if (monitor != null) {
+ buf.append(indstr1).append("}").append(new_line_separator);
+ }
+
+ buf.append(indstr).append("}").append(new_line_separator);
+
+ return buf.toString();
+ }
+
+ public void replaceStatement(Statement oldstat, Statement newstat) {
+
+ if (handler == oldstat) {
+ handler = newstat;
+ }
+
+ super.replaceStatement(oldstat, newstat);
+ }
+
+ public Statement getSimpleCopy() {
+
+ CatchAllStatement cas = new CatchAllStatement();
+
+ cas.isFinally = this.isFinally;
+
+ if (this.monitor != null) {
+ cas.monitor = new VarExprent(DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER),
+ VarType.VARTYPE_INT,
+ (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR));
+ }
+
+ if (!this.vars.isEmpty()) {
+ // FIXME: WTF??? vars?!
+ vars.add(new VarExprent(DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER),
+ new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Throwable"),
+ (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR)));
+ }
+
+ return cas;
+ }
+
+ public void initSimpleCopy() {
+ first = stats.get(0);
+ handler = stats.get(1);
+ }
+
+ // *****************************************************************************
+ // getter and setter methods
+ // *****************************************************************************
+
+ public Statement getHandler() {
+ return handler;
+ }
+
+
+ public void setHandler(Statement handler) {
+ this.handler = handler;
+ }
+
+
+ public boolean isFinally() {
+ return isFinally;
+ }
+
+
+ public void setFinally(boolean isFinally) {
+ this.isFinally = isFinally;
+ }
+
+
+ public VarExprent getMonitor() {
+ return monitor;
+ }
+
+
+ public void setMonitor(VarExprent monitor) {
+ this.monitor = monitor;
+ }
+
+ public List<VarExprent> getVars() {
+ return vars;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java
new file mode 100644
index 000000000000..ea45bdda75d7
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java
@@ -0,0 +1,210 @@
+/*
+ * 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.modules.decompiler.stats;
+
+import org.jetbrains.java.decompiler.code.CodeConstants;
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
+import org.jetbrains.java.decompiler.modules.decompiler.DecHelper;
+import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
+import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;
+import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+public class CatchStatement extends Statement {
+
+ private List<List<String>> exctstrings = new ArrayList<List<String>>();
+
+ private List<VarExprent> vars = new ArrayList<VarExprent>();
+
+ // *****************************************************************************
+ // constructors
+ // *****************************************************************************
+
+ private CatchStatement() {
+ type = TYPE_TRYCATCH;
+ }
+
+ private CatchStatement(Statement head, Statement next, HashSet<Statement> setHandlers) {
+
+ this();
+
+ first = head;
+ stats.addWithKey(first, first.id);
+
+ for (StatEdge edge : head.getSuccessorEdges(StatEdge.TYPE_EXCEPTION)) {
+ Statement stat = edge.getDestination();
+
+ if (setHandlers.contains(stat)) {
+ stats.addWithKey(stat, stat.id);
+ exctstrings.add(new ArrayList<String>(edge.getExceptions()));
+
+ vars.add(new VarExprent(DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER),
+ new VarType(CodeConstants.TYPE_OBJECT, 0, edge.getExceptions().get(0)),
+ // FIXME: for now simply the first type. Should get the first common superclass when possible.
+ (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR)));
+ }
+ }
+
+ if (next != null) {
+ post = next;
+ }
+ }
+
+ // *****************************************************************************
+ // public methods
+ // *****************************************************************************
+
+ public static Statement isHead(Statement head) {
+
+ if (head.getLastBasicType() != LASTBASICTYPE_GENERAL) {
+ return null;
+ }
+
+ HashSet<Statement> setHandlers = DecHelper.getUniquePredExceptions(head);
+
+ if (!setHandlers.isEmpty()) {
+
+ int hnextcount = 0; // either no statements with connection to next, or more than 1
+
+ Statement next = null;
+ List<StatEdge> lstHeadSuccs = head.getSuccessorEdges(STATEDGE_DIRECT_ALL);
+ if (!lstHeadSuccs.isEmpty() && lstHeadSuccs.get(0).getType() == StatEdge.TYPE_REGULAR) {
+ next = lstHeadSuccs.get(0).getDestination();
+ hnextcount = 2;
+ }
+
+ for (StatEdge edge : head.getSuccessorEdges(StatEdge.TYPE_EXCEPTION)) {
+ Statement stat = edge.getDestination();
+
+ boolean handlerok = true;
+
+ if (edge.getExceptions() != null && setHandlers.contains(stat)) {
+ if (stat.getLastBasicType() != LASTBASICTYPE_GENERAL) {
+ handlerok = false;
+ }
+ else {
+ List<StatEdge> lstStatSuccs = stat.getSuccessorEdges(STATEDGE_DIRECT_ALL);
+ if (!lstStatSuccs.isEmpty() && lstStatSuccs.get(0).getType() == StatEdge.TYPE_REGULAR) {
+
+ Statement statn = lstStatSuccs.get(0).getDestination();
+
+ if (next == null) {
+ next = statn;
+ }
+ else if (next != statn) {
+ handlerok = false;
+ }
+
+ if (handlerok) {
+ hnextcount++;
+ }
+ }
+ }
+ }
+ else {
+ handlerok = false;
+ }
+
+ if (!handlerok) {
+ setHandlers.remove(stat);
+ }
+ }
+
+ if (hnextcount != 1 && !setHandlers.isEmpty()) {
+ List<Statement> lst = new ArrayList<Statement>();
+ lst.add(head);
+ lst.addAll(setHandlers);
+
+ for (Statement st : lst) {
+ if (st.isMonitorEnter()) {
+ return null;
+ }
+ }
+
+ if (DecHelper.checkStatementExceptions(lst)) {
+ return new CatchStatement(head, next, setHandlers);
+ }
+ }
+ }
+ return null;
+ }
+
+ public String toJava(int indent) {
+ String indstr = InterpreterUtil.getIndentString(indent);
+ StringBuilder buf = new StringBuilder();
+
+ String new_line_separator = DecompilerContext.getNewLineSeparator();
+
+ buf.append(ExprProcessor.listToJava(varDefinitions, indent));
+
+ if (isLabeled()) {
+ buf.append(indstr).append("label").append(this.id).append(":").append(new_line_separator);
+ }
+
+ buf.append(indstr).append("try {").append(new_line_separator);
+ buf.append(ExprProcessor.jmpWrapper(first, indent + 1, true));
+ buf.append(indstr).append("}");
+
+ for (int i = 1; i < stats.size(); i++) {
+ List<String> exception_types = exctstrings.get(i - 1);
+
+ buf.append(" catch (");
+ if (exception_types.size() > 1) { // multi-catch, Java 7 style
+ for (int exc_index = 1; exc_index < exception_types.size(); ++exc_index) {
+ VarType exc_type = new VarType(CodeConstants.TYPE_OBJECT, 0, exception_types.get(exc_index));
+ String exc_type_name = ExprProcessor.getCastTypeName(exc_type);
+
+ buf.append(exc_type_name).append(" | ");
+ }
+ }
+ buf.append(vars.get(i - 1).toJava(indent));
+ buf.append(") {").append(new_line_separator).append(ExprProcessor.jmpWrapper(stats.get(i), indent + 1, true)).append(indstr)
+ .append("}");
+ }
+ buf.append(new_line_separator);
+
+ return buf.toString();
+ }
+
+ public Statement getSimpleCopy() {
+
+ CatchStatement cs = new CatchStatement();
+
+ for (List<String> exc : this.exctstrings) {
+ cs.exctstrings.add(new ArrayList<String>(exc));
+ cs.vars.add(new VarExprent(DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER),
+ new VarType(CodeConstants.TYPE_OBJECT, 0, exc.get(0)),
+ (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR)));
+ }
+
+ return cs;
+ }
+
+ // *****************************************************************************
+ // getter and setter methods
+ // *****************************************************************************
+
+ public List<VarExprent> getVars() {
+ return vars;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java
new file mode 100644
index 000000000000..7c152c21eb1f
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java
@@ -0,0 +1,222 @@
+/*
+ * 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.modules.decompiler.stats;
+
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
+import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class DoStatement extends Statement {
+
+ public static final int LOOP_DO = 0;
+ public static final int LOOP_DOWHILE = 1;
+ public static final int LOOP_WHILE = 2;
+ public static final int LOOP_FOR = 3;
+
+ private int looptype;
+
+ private List<Exprent> initExprent = new ArrayList<Exprent>();
+ private List<Exprent> conditionExprent = new ArrayList<Exprent>();
+ private List<Exprent> incExprent = new ArrayList<Exprent>();
+
+ // *****************************************************************************
+ // constructors
+ // *****************************************************************************
+
+ private DoStatement() {
+ type = Statement.TYPE_DO;
+ looptype = LOOP_DO;
+
+ initExprent.add(null);
+ conditionExprent.add(null);
+ incExprent.add(null);
+ }
+
+ private DoStatement(Statement head) {
+
+ this();
+
+ first = head;
+ stats.addWithKey(first, first.id);
+
+ // post is always null!
+ }
+
+ // *****************************************************************************
+ // public methods
+ // *****************************************************************************
+
+ public static Statement isHead(Statement head) {
+
+ if (head.getLastBasicType() == LASTBASICTYPE_GENERAL && !head.isMonitorEnter()) {
+
+ // at most one outgoing edge
+ StatEdge edge = null;
+ List<StatEdge> lstSuccs = head.getSuccessorEdges(STATEDGE_DIRECT_ALL);
+ if (!lstSuccs.isEmpty()) {
+ edge = lstSuccs.get(0);
+ }
+
+ // regular loop
+ if (edge != null && edge.getType() == StatEdge.TYPE_REGULAR && edge.getDestination() == head) {
+ return new DoStatement(head);
+ }
+
+ // continues
+ if (head.type != TYPE_DO && (edge == null || edge.getType() != StatEdge.TYPE_REGULAR) &&
+ head.getContinueSet().contains(head.getBasichead())) {
+ return new DoStatement(head);
+ }
+ }
+
+ return null;
+ }
+
+ public String toJava(int indent) {
+ String indstr = InterpreterUtil.getIndentString(indent);
+ StringBuilder buf = new StringBuilder();
+
+ String new_line_separator = DecompilerContext.getNewLineSeparator();
+
+ buf.append(ExprProcessor.listToJava(varDefinitions, indent));
+
+ if (isLabeled()) {
+ buf.append(indstr).append("label").append(this.id).append(":").append(new_line_separator);
+ }
+
+ switch (looptype) {
+ case LOOP_DO:
+ buf.append(indstr).append("while(true) {").append(new_line_separator);
+ buf.append(ExprProcessor.jmpWrapper(first, indent + 1, true));
+ buf.append(indstr).append("}").append(new_line_separator);
+ break;
+ case LOOP_DOWHILE:
+ buf.append(indstr).append("do {").append(new_line_separator);
+ buf.append(ExprProcessor.jmpWrapper(first, indent + 1, true));
+ buf.append(indstr).append("} while(").append(conditionExprent.get(0).toJava(indent)).append(");").append(new_line_separator);
+ break;
+ case LOOP_WHILE:
+ buf.append(indstr).append("while(").append(conditionExprent.get(0).toJava(indent)).append(") {").append(new_line_separator);
+ buf.append(ExprProcessor.jmpWrapper(first, indent + 1, true));
+ buf.append(indstr).append("}").append(new_line_separator);
+ break;
+ case LOOP_FOR:
+ buf.append(indstr).append("for(").append(initExprent.get(0) == null ? "" : initExprent.get(0).toJava(indent)).append("; ")
+ .append(conditionExprent.get(0).toJava(indent)).append("; ").append(incExprent.get(0).toJava(indent)).append(") {")
+ .append(new_line_separator);
+ buf.append(ExprProcessor.jmpWrapper(first, indent + 1, true));
+ buf.append(indstr).append("}").append(new_line_separator);
+ }
+
+ return buf.toString();
+ }
+
+ public List<Object> getSequentialObjects() {
+
+ List<Object> lst = new ArrayList<Object>();
+
+ switch (looptype) {
+ case LOOP_FOR:
+ if (getInitExprent() != null) {
+ lst.add(getInitExprent());
+ }
+ case LOOP_WHILE:
+ lst.add(getConditionExprent());
+ }
+
+ lst.add(first);
+
+ switch (looptype) {
+ case LOOP_DOWHILE:
+ lst.add(getConditionExprent());
+ break;
+ case LOOP_FOR:
+ lst.add(getIncExprent());
+ }
+
+ return lst;
+ }
+
+ public void replaceExprent(Exprent oldexpr, Exprent newexpr) {
+ if (initExprent.get(0) == oldexpr) {
+ initExprent.set(0, newexpr);
+ }
+ if (conditionExprent.get(0) == oldexpr) {
+ conditionExprent.set(0, newexpr);
+ }
+ if (incExprent.get(0) == oldexpr) {
+ incExprent.set(0, newexpr);
+ }
+ }
+
+ public Statement getSimpleCopy() {
+ return new DoStatement();
+ }
+
+ // *****************************************************************************
+ // getter and setter methods
+ // *****************************************************************************
+
+ public List<Exprent> getInitExprentList() {
+ return initExprent;
+ }
+
+ public List<Exprent> getConditionExprentList() {
+ return conditionExprent;
+ }
+
+ public List<Exprent> getIncExprentList() {
+ return incExprent;
+ }
+
+ public Exprent getConditionExprent() {
+ return conditionExprent.get(0);
+ }
+
+ public void setConditionExprent(Exprent conditionExprent) {
+ this.conditionExprent.set(0, conditionExprent);
+ }
+
+ public Exprent getIncExprent() {
+ return incExprent.get(0);
+ }
+
+ public void setIncExprent(Exprent incExprent) {
+ this.incExprent.set(0, incExprent);
+ }
+
+ public Exprent getInitExprent() {
+ return initExprent.get(0);
+ }
+
+ public void setInitExprent(Exprent initExprent) {
+ this.initExprent.set(0, initExprent);
+ }
+
+ public int getLooptype() {
+ return looptype;
+ }
+
+ public void setLooptype(int looptype) {
+ this.looptype = looptype;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/GeneralStatement.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/GeneralStatement.java
new file mode 100644
index 000000000000..a11171ef3010
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/GeneralStatement.java
@@ -0,0 +1,74 @@
+/*
+ * 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.modules.decompiler.stats;
+
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+
+import java.util.Collection;
+import java.util.HashSet;
+
+
+public class GeneralStatement extends Statement {
+
+ // *****************************************************************************
+ // constructors
+ // *****************************************************************************
+
+ private GeneralStatement() {
+ type = Statement.TYPE_GENERAL;
+ }
+
+ public GeneralStatement(Statement head, Collection<Statement> statements, Statement post) {
+
+ this();
+
+ first = head;
+ stats.addWithKey(head, head.id);
+
+ HashSet<Statement> set = new HashSet<Statement>(statements);
+ set.remove(head);
+
+ for (Statement st : set) {
+ stats.addWithKey(st, st.id);
+ }
+
+ this.post = post;
+ }
+
+ // *****************************************************************************
+ // public methods
+ // *****************************************************************************
+
+ public String toJava(int indent) {
+ String indstr = InterpreterUtil.getIndentString(indent);
+ StringBuilder buf = new StringBuilder();
+
+ String new_line_separator = DecompilerContext.getNewLineSeparator();
+
+ if (isLabeled()) {
+ buf.append(indstr).append("label").append(this.id).append(":").append(new_line_separator);
+ }
+
+ buf.append(indstr).append("abstract statement {").append(new_line_separator);
+ for (int i = 0; i < stats.size(); i++) {
+ buf.append(stats.get(i).toJava(indent + 1));
+ }
+ buf.append(indstr).append("}");
+
+ return buf.toString();
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java
new file mode 100644
index 000000000000..9ad812534c6c
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java
@@ -0,0 +1,416 @@
+/*
+ * 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.modules.decompiler.stats;
+
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.modules.decompiler.DecHelper;
+import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
+import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.IfExprent;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class IfStatement extends Statement {
+
+ public static int IFTYPE_IF = 0;
+ public static int IFTYPE_IFELSE = 1;
+
+ public int iftype;
+
+ // *****************************************************************************
+ // private fields
+ // *****************************************************************************
+
+ private Statement ifstat;
+ private Statement elsestat;
+
+ private StatEdge ifedge;
+ private StatEdge elseedge;
+
+ private boolean negated = false;
+
+ private boolean iffflag;
+
+ private List<Exprent> headexprent = new ArrayList<Exprent>(); // contains IfExprent
+
+ // *****************************************************************************
+ // constructors
+ // *****************************************************************************
+
+ private IfStatement() {
+ type = TYPE_IF;
+
+ headexprent.add(null);
+ }
+
+ private IfStatement(Statement head, int regedges, Statement postst) {
+
+ this();
+
+ first = head;
+ stats.addWithKey(head, head.id);
+
+ List<StatEdge> lstHeadSuccs = head.getSuccessorEdges(STATEDGE_DIRECT_ALL);
+
+ switch (regedges) {
+ case 0:
+ ifstat = null;
+ elsestat = null;
+
+ break;
+ case 1:
+ ifstat = null;
+ elsestat = null;
+
+ StatEdge edgeif = lstHeadSuccs.get(1);
+ if (edgeif.getType() != StatEdge.TYPE_REGULAR) {
+ post = lstHeadSuccs.get(0).getDestination();
+ }
+ else {
+ post = edgeif.getDestination();
+ negated = true;
+ }
+ break;
+ case 2:
+ elsestat = lstHeadSuccs.get(0).getDestination();
+ ifstat = lstHeadSuccs.get(1).getDestination();
+
+ List<StatEdge> lstSucc = ifstat.getSuccessorEdges(StatEdge.TYPE_REGULAR);
+ List<StatEdge> lstSucc1 = elsestat.getSuccessorEdges(StatEdge.TYPE_REGULAR);
+
+ if (ifstat.getPredecessorEdges(StatEdge.TYPE_REGULAR).size() > 1 || lstSucc.size() > 1) {
+ post = ifstat;
+ }
+ else if (elsestat.getPredecessorEdges(StatEdge.TYPE_REGULAR).size() > 1 || lstSucc1.size() > 1) {
+ post = elsestat;
+ }
+ else {
+ if (lstSucc.size() == 0) {
+ post = elsestat;
+ }
+ else if (lstSucc1.size() == 0) {
+ post = ifstat;
+ }
+ }
+
+ if (ifstat == post) {
+ if (elsestat != post) {
+ ifstat = elsestat;
+ negated = true;
+ }
+ else {
+ ifstat = null;
+ }
+ elsestat = null;
+ }
+ else if (elsestat == post) {
+ elsestat = null;
+ }
+ else {
+ post = postst;
+ }
+
+ if (elsestat == null) {
+ regedges = 1; // if without else
+ }
+ }
+
+ ifedge = lstHeadSuccs.get(negated ? 0 : 1);
+ elseedge = (regedges == 2) ? lstHeadSuccs.get(negated ? 1 : 0) : null;
+
+ iftype = (regedges == 2) ? IFTYPE_IFELSE : IFTYPE_IF;
+
+ if (iftype == IFTYPE_IF) {
+ if (regedges == 0) {
+ StatEdge edge = lstHeadSuccs.get(0);
+ head.removeSuccessor(edge);
+ edge.setSource(this);
+ this.addSuccessor(edge);
+ }
+ else if (regedges == 1) {
+ StatEdge edge = lstHeadSuccs.get(negated ? 1 : 0);
+ head.removeSuccessor(edge);
+ }
+ }
+
+ if (ifstat != null) {
+ stats.addWithKey(ifstat, ifstat.id);
+ }
+
+ if (elsestat != null) {
+ stats.addWithKey(elsestat, elsestat.id);
+ }
+
+ if (post == head) {
+ post = this;
+ }
+ }
+
+
+ // *****************************************************************************
+ // public methods
+ // *****************************************************************************
+
+ public static Statement isHead(Statement head) {
+
+ if (head.type == TYPE_BASICBLOCK && head.getLastBasicType() == LASTBASICTYPE_IF) {
+ int regsize = head.getSuccessorEdges(StatEdge.TYPE_REGULAR).size();
+
+ Statement p = null;
+
+ boolean ok = (regsize < 2);
+ if (!ok) {
+ List<Statement> lst = new ArrayList<Statement>();
+ if (DecHelper.isChoiceStatement(head, lst)) {
+ p = lst.remove(0);
+
+ for (Statement st : lst) {
+ if (st.isMonitorEnter()) {
+ return null;
+ }
+ }
+
+ ok = DecHelper.checkStatementExceptions(lst);
+ }
+ }
+
+ if (ok) {
+ return new IfStatement(head, regsize, p);
+ }
+ }
+
+ return null;
+ }
+
+ public String toJava(int indent) {
+ String indstr = InterpreterUtil.getIndentString(indent);
+ StringBuilder buf = new StringBuilder();
+
+ String new_line_separator = DecompilerContext.getNewLineSeparator();
+
+ buf.append(ExprProcessor.listToJava(varDefinitions, indent));
+ buf.append(first.toJava(indent));
+
+ if (isLabeled()) {
+ buf.append(indstr).append("label").append(this.id).append(":").append(new_line_separator);
+ }
+
+ buf.append(indstr).append(headexprent.get(0).toJava(indent)).append(" {").append(new_line_separator);
+
+ if (ifstat == null) {
+ buf.append(InterpreterUtil.getIndentString(indent + 1));
+
+ if (ifedge.explicit) {
+ if (ifedge.getType() == StatEdge.TYPE_BREAK) {
+ // break
+ buf.append("break");
+ }
+ else {
+ // continue
+ buf.append("continue");
+ }
+
+ if (ifedge.labeled) {
+ buf.append(" label").append(ifedge.closure.id);
+ }
+ }
+ buf.append(";").append(new_line_separator);
+ }
+ else {
+ buf.append(ExprProcessor.jmpWrapper(ifstat, indent + 1, true));
+ }
+
+ boolean elseif = false;
+
+ if (elsestat != null) {
+ if (elsestat.type == Statement.TYPE_IF
+ && elsestat.varDefinitions.isEmpty() && elsestat.getFirst().getExprents().isEmpty() &&
+ !elsestat.isLabeled() &&
+ (elsestat.getSuccessorEdges(STATEDGE_DIRECT_ALL).isEmpty()
+ || !elsestat.getSuccessorEdges(STATEDGE_DIRECT_ALL).get(0).explicit)) { // else if
+ String content = ExprProcessor.jmpWrapper(elsestat, indent, false);
+ content = content.substring(indstr.length());
+
+ buf.append(indstr).append("} else ");
+ buf.append(content);
+
+ elseif = true;
+ }
+ else {
+ String content = ExprProcessor.jmpWrapper(elsestat, indent + 1, false);
+
+ if (content.length() > 0) {
+ buf.append(indstr).append("} else {").append(new_line_separator);
+ buf.append(content);
+ }
+ }
+ }
+
+ if (!elseif) {
+ buf.append(indstr).append("}").append(new_line_separator);
+ }
+
+ return buf.toString();
+ }
+
+ public void initExprents() {
+
+ IfExprent ifexpr = (IfExprent)first.getExprents().remove(first.getExprents().size() - 1);
+
+ if (negated) {
+ ifexpr = (IfExprent)ifexpr.copy();
+ ifexpr.negateIf();
+ }
+
+ headexprent.set(0, ifexpr);
+ }
+
+ public List<Object> getSequentialObjects() {
+
+ List<Object> lst = new ArrayList<Object>(stats);
+ lst.add(1, headexprent.get(0));
+
+ return lst;
+ }
+
+ public void replaceExprent(Exprent oldexpr, Exprent newexpr) {
+ if (headexprent.get(0) == oldexpr) {
+ headexprent.set(0, newexpr);
+ }
+ }
+
+ public void replaceStatement(Statement oldstat, Statement newstat) {
+
+ super.replaceStatement(oldstat, newstat);
+
+ if (ifstat == oldstat) {
+ ifstat = newstat;
+ }
+
+ if (elsestat == oldstat) {
+ elsestat = newstat;
+ }
+
+ List<StatEdge> lstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL);
+
+ if (iftype == IFTYPE_IF) {
+ ifedge = lstSuccs.get(0);
+ elseedge = null;
+ }
+ else {
+ StatEdge edge0 = lstSuccs.get(0);
+ StatEdge edge1 = lstSuccs.get(1);
+ if (edge0.getDestination() == ifstat) {
+ ifedge = edge0;
+ elseedge = edge1;
+ }
+ else {
+ ifedge = edge1;
+ elseedge = edge0;
+ }
+ }
+ }
+
+ public Statement getSimpleCopy() {
+
+ IfStatement is = new IfStatement();
+ is.iftype = this.iftype;
+ is.negated = this.negated;
+ is.iffflag = this.iffflag;
+
+ return is;
+ }
+
+ public void initSimpleCopy() {
+
+ first = stats.get(0);
+
+ List<StatEdge> lstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL);
+ ifedge = lstSuccs.get((iftype == IFTYPE_IF || negated) ? 0 : 1);
+ if (stats.size() > 1) {
+ ifstat = stats.get(1);
+ }
+
+ if (iftype == IFTYPE_IFELSE) {
+ elseedge = lstSuccs.get(negated ? 1 : 0);
+ elsestat = stats.get(2);
+ }
+ }
+
+ // *****************************************************************************
+ // getter and setter methods
+ // *****************************************************************************
+
+ public Statement getElsestat() {
+ return elsestat;
+ }
+
+ public void setElsestat(Statement elsestat) {
+ this.elsestat = elsestat;
+ }
+
+ public Statement getIfstat() {
+ return ifstat;
+ }
+
+ public void setIfstat(Statement ifstat) {
+ this.ifstat = ifstat;
+ }
+
+ public boolean isNegated() {
+ return negated;
+ }
+
+ public void setNegated(boolean negated) {
+ this.negated = negated;
+ }
+
+ public List<Exprent> getHeadexprentList() {
+ return headexprent;
+ }
+
+ public IfExprent getHeadexprent() {
+ return (IfExprent)headexprent.get(0);
+ }
+
+ public boolean isIffflag() {
+ return iffflag;
+ }
+
+ public void setIffflag(boolean iffflag) {
+ this.iffflag = iffflag;
+ }
+
+ public void setElseEdge(StatEdge elseedge) {
+ this.elseedge = elseedge;
+ }
+
+ public void setIfEdge(StatEdge ifedge) {
+ this.ifedge = ifedge;
+ }
+
+ public StatEdge getIfEdge() {
+ return ifedge;
+ }
+
+ public StatEdge getElseEdge() {
+ return elseedge;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/RootStatement.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/RootStatement.java
new file mode 100644
index 000000000000..2a6944712ace
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/RootStatement.java
@@ -0,0 +1,48 @@
+/*
+ * 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.modules.decompiler.stats;
+
+import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
+
+
+public class RootStatement extends Statement {
+
+ private Statement dummyExit;
+
+ public RootStatement(Statement head, Statement dummyExit) {
+
+ type = Statement.TYPE_ROOT;
+
+ first = head;
+ this.dummyExit = dummyExit;
+
+ stats.addWithKey(first, first.id);
+ first.setParent(this);
+ }
+
+ public String toJava(int indent) {
+ return ExprProcessor.listToJava(varDefinitions, indent) +
+ first.toJava(indent);
+ }
+
+ public Statement getDummyExit() {
+ return dummyExit;
+ }
+
+ public void setDummyExit(Statement dummyExit) {
+ this.dummyExit = dummyExit;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java
new file mode 100644
index 000000000000..eccce91fd051
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java
@@ -0,0 +1,144 @@
+/*
+ * 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.modules.decompiler.stats;
+
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.modules.decompiler.DecHelper;
+import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
+import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+
+import java.util.Arrays;
+import java.util.List;
+
+
+public class SequenceStatement extends Statement {
+
+
+ // *****************************************************************************
+ // constructors
+ // *****************************************************************************
+
+ private SequenceStatement() {
+ type = Statement.TYPE_SEQUENCE;
+ }
+
+ public SequenceStatement(List<Statement> lst) {
+
+ this();
+
+ lastBasicType = lst.get(lst.size() - 1).getLastBasicType();
+
+ for (Statement st : lst) {
+ stats.addWithKey(st, st.id);
+ }
+
+ first = stats.get(0);
+ }
+
+ private SequenceStatement(Statement head, Statement tail) {
+
+ this(Arrays.asList(head, tail));
+
+ List<StatEdge> lstSuccs = tail.getSuccessorEdges(STATEDGE_DIRECT_ALL);
+ if (!lstSuccs.isEmpty()) {
+ StatEdge edge = lstSuccs.get(0);
+
+ if (edge.getType() == StatEdge.TYPE_REGULAR && edge.getDestination() != head) {
+ post = edge.getDestination();
+ }
+ }
+ }
+
+
+ // *****************************************************************************
+ // public methods
+ // *****************************************************************************
+
+ public static Statement isHead2Block(Statement head) {
+
+ if (head.getLastBasicType() != Statement.LASTBASICTYPE_GENERAL) {
+ return null;
+ }
+
+ // at most one outgoing edge
+ StatEdge edge = null;
+ List<StatEdge> lstSuccs = head.getSuccessorEdges(STATEDGE_DIRECT_ALL);
+ if (!lstSuccs.isEmpty()) {
+ edge = lstSuccs.get(0);
+ }
+
+ if (edge != null && edge.getType() == StatEdge.TYPE_REGULAR) {
+ Statement stat = edge.getDestination();
+
+ if (stat != head && stat.getPredecessorEdges(StatEdge.TYPE_REGULAR).size() == 1
+ && !stat.isMonitorEnter()) {
+
+ if (stat.getLastBasicType() == Statement.LASTBASICTYPE_GENERAL) {
+ if (DecHelper.checkStatementExceptions(Arrays.asList(head, stat))) {
+ return new SequenceStatement(head, stat);
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public String toJava(int indent) {
+
+ StringBuilder buf = new StringBuilder();
+
+ String indstr = null;
+ boolean islabeled = isLabeled();
+
+ String new_line_separator = DecompilerContext.getNewLineSeparator();
+
+ buf.append(ExprProcessor.listToJava(varDefinitions, indent));
+
+ if (islabeled) {
+ indstr = InterpreterUtil.getIndentString(indent);
+ indent++;
+ buf.append(indstr).append("label").append(this.id).append(": {").append(new_line_separator);
+ }
+
+ boolean notempty = false;
+
+ for (int i = 0; i < stats.size(); i++) {
+
+ Statement st = stats.get(i);
+
+ if (i > 0 && notempty) {
+ buf.append(new_line_separator);
+ }
+
+ String str = ExprProcessor.jmpWrapper(st, indent, false);
+ buf.append(str);
+
+ notempty = (str.trim().length() > 0);
+ }
+
+ if (islabeled) {
+ buf.append(indstr).append("}").append(new_line_separator);
+ }
+
+ return buf.toString();
+ }
+
+ public Statement getSimpleCopy() {
+ return new SequenceStatement();
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java
new file mode 100644
index 000000000000..cbdb3b71ff22
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java
@@ -0,0 +1,863 @@
+/*
+ * 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.modules.decompiler.stats;
+
+import org.jetbrains.java.decompiler.code.CodeConstants;
+import org.jetbrains.java.decompiler.code.InstructionSequence;
+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.modules.decompiler.StatEdge;
+import org.jetbrains.java.decompiler.modules.decompiler.StrongConnectivityHelper;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
+import org.jetbrains.java.decompiler.util.VBStyleCollection;
+
+import java.util.*;
+
+public class Statement {
+
+ public static final int STATEDGE_ALL = 1 << 31;
+ public static final int STATEDGE_DIRECT_ALL = 1 << 30;
+
+ public static final int DIRECTION_BACKWARD = 0;
+ public static final int DIRECTION_FORWARD = 1;
+
+ public static final int TYPE_GENERAL = 0;
+ public static final int TYPE_IF = 2;
+ public static final int TYPE_DO = 5;
+ public static final int TYPE_SWITCH = 6;
+ public static final int TYPE_TRYCATCH = 7;
+ public static final int TYPE_BASICBLOCK = 8;
+ public static final int TYPE_FINALLY = 9;
+ public static final int TYPE_SYNCRONIZED = 10;
+ public static final int TYPE_PLACEHOLDER = 11;
+ public static final int TYPE_CATCHALL = 12;
+ public static final int TYPE_ROOT = 13;
+ public static final int TYPE_DUMMYEXIT = 14;
+ public static final int TYPE_SEQUENCE = 15;
+
+
+ public static final int LASTBASICTYPE_IF = 0;
+ public static final int LASTBASICTYPE_SWITCH = 1;
+ public static final int LASTBASICTYPE_GENERAL = 2;
+
+
+ // *****************************************************************************
+ // public fields
+ // *****************************************************************************
+
+ public int type;
+
+ public Integer id;
+
+ // *****************************************************************************
+ // private fields
+ // *****************************************************************************
+
+ private Map<Integer, List<StatEdge>> mapSuccEdges = new HashMap<Integer, List<StatEdge>>();
+ private Map<Integer, List<StatEdge>> mapPredEdges = new HashMap<Integer, List<StatEdge>>();
+
+ private Map<Integer, List<Statement>> mapSuccStates = new HashMap<Integer, List<Statement>>();
+ private Map<Integer, List<Statement>> mapPredStates = new HashMap<Integer, List<Statement>>();
+
+ // statement as graph
+ protected VBStyleCollection<Statement, Integer> stats = new VBStyleCollection<Statement, Integer>();
+
+ protected Statement parent;
+
+ protected Statement first;
+
+ protected List<Exprent> exprents;
+
+ protected HashSet<StatEdge> labelEdges = new HashSet<StatEdge>();
+
+ protected List<Exprent> varDefinitions = new ArrayList<Exprent>();
+
+ // copied statement, s. deobfuscating of irreducible CFGs
+ private boolean copied = false;
+
+ // relevant for the first stage of processing only
+ // set to null after initializing of the statement structure
+
+ protected Statement post;
+
+ protected int lastBasicType = LASTBASICTYPE_GENERAL;
+
+ protected boolean isMonitorEnter;
+
+ protected boolean containsMonitorExit;
+
+ protected HashSet<Statement> continueSet = new HashSet<Statement>();
+
+ // *****************************************************************************
+ // initializers
+ // *****************************************************************************
+
+ {
+ // set statement id
+ id = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER);
+ }
+
+ // *****************************************************************************
+ // public methods
+ // *****************************************************************************
+
+ public void clearTempInformation() {
+
+ post = null;
+ continueSet = null;
+
+ copied = false;
+ // FIXME: used in FlattenStatementsHelper.flattenStatement()! check and remove
+ //lastBasicType = LASTBASICTYPE_GENERAL;
+ isMonitorEnter = false;
+ containsMonitorExit = false;
+
+ processMap(mapSuccEdges);
+ processMap(mapPredEdges);
+ processMap(mapSuccStates);
+ processMap(mapPredStates);
+ }
+
+ private static <T> void processMap(Map<Integer, List<T>> map) {
+ map.remove(StatEdge.TYPE_EXCEPTION);
+
+ List<T> lst = map.get(STATEDGE_DIRECT_ALL);
+ if (lst != null) {
+ map.put(STATEDGE_ALL, new ArrayList<T>(lst));
+ }
+ else {
+ map.remove(STATEDGE_ALL);
+ }
+ }
+
+ public void collapseNodesToStatement(Statement stat) {
+
+ Statement head = stat.getFirst();
+ Statement post = stat.getPost();
+
+ VBStyleCollection<Statement, Integer> setNodes = stat.getStats();
+
+ // post edges
+ if (post != null) {
+ for (StatEdge edge : post.getEdges(STATEDGE_DIRECT_ALL, DIRECTION_BACKWARD)) {
+ if (stat.containsStatementStrict(edge.getSource())) {
+ edge.getSource().changeEdgeType(DIRECTION_FORWARD, edge, StatEdge.TYPE_BREAK);
+ stat.addLabeledEdge(edge);
+ }
+ }
+ }
+
+ // regular head edges
+ for (StatEdge prededge : head.getAllPredecessorEdges()) {
+
+ if (prededge.getType() != StatEdge.TYPE_EXCEPTION &&
+ stat.containsStatementStrict(prededge.getSource())) {
+ prededge.getSource().changeEdgeType(DIRECTION_FORWARD, prededge, StatEdge.TYPE_CONTINUE);
+ stat.addLabeledEdge(prededge);
+ }
+
+ head.removePredecessor(prededge);
+ prededge.getSource().changeEdgeNode(DIRECTION_FORWARD, prededge, stat);
+ stat.addPredecessor(prededge);
+ }
+
+ if (setNodes.containsKey(first.id)) {
+ first = stat;
+ }
+
+ // exception edges
+ Set<Statement> setHandlers = new HashSet<Statement>(head.getNeighbours(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD));
+ for (Statement node : setNodes) {
+ setHandlers.retainAll(node.getNeighbours(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD));
+ }
+
+ if (!setHandlers.isEmpty()) {
+
+ for (StatEdge edge : head.getEdges(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD)) {
+ Statement handler = edge.getDestination();
+
+ if (setHandlers.contains(handler)) {
+ if (!setNodes.containsKey(handler.id)) {
+ stat.addSuccessor(new StatEdge(stat, handler, edge.getExceptions()));
+ }
+ }
+ }
+
+ for (Statement node : setNodes) {
+ for (StatEdge edge : node.getEdges(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD)) {
+ if (setHandlers.contains(edge.getDestination())) {
+ node.removeSuccessor(edge);
+ }
+ }
+ }
+ }
+
+ if (post != null &&
+ !stat.getNeighbours(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD).contains(post)) { // TODO: second condition redundant?
+ stat.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, stat, post));
+ }
+
+
+ // adjust statement collection
+ for (Statement st : setNodes) {
+ stats.removeWithKey(st.id);
+ }
+
+ stats.addWithKey(stat, stat.id);
+
+ stat.setAllParent();
+ stat.setParent(this);
+
+ stat.buildContinueSet();
+ // monitorenter and monitorexit
+ stat.buildMonitorFlags();
+
+ if (stat.type == TYPE_SWITCH) {
+ // special case switch, sorting leaf nodes
+ ((SwitchStatement)stat).sortEdgesAndNodes();
+ }
+ }
+
+ public void setAllParent() {
+ for (Statement st : stats) {
+ st.setParent(this);
+ }
+ }
+
+ public void addLabeledEdge(StatEdge edge) {
+
+ if (edge.closure != null) {
+ edge.closure.getLabelEdges().remove(edge);
+ }
+ edge.closure = this;
+ this.getLabelEdges().add(edge);
+ }
+
+ private void addEdgeDirectInternal(int direction, StatEdge edge, int edgetype) {
+
+ Map<Integer, List<StatEdge>> mapEdges = direction == DIRECTION_BACKWARD ? mapPredEdges : mapSuccEdges;
+ Map<Integer, List<Statement>> mapStates = direction == DIRECTION_BACKWARD ? mapPredStates : mapSuccStates;
+
+ List<StatEdge> lst = mapEdges.get(edgetype);
+ if (lst == null) {
+ mapEdges.put(edgetype, lst = new ArrayList<StatEdge>());
+ }
+ lst.add(edge);
+
+ List<Statement> lstStates = mapStates.get(edgetype);
+ if (lstStates == null) {
+ mapStates.put(edgetype, lstStates = new ArrayList<Statement>());
+ }
+ lstStates.add(direction == DIRECTION_BACKWARD ? edge.getSource() : edge.getDestination());
+ }
+
+ private void addEdgeInternal(int direction, StatEdge edge) {
+
+ int type = edge.getType();
+
+ int[] arrtypes;
+ if (type == StatEdge.TYPE_EXCEPTION) {
+ arrtypes = new int[]{STATEDGE_ALL, StatEdge.TYPE_EXCEPTION};
+ }
+ else {
+ arrtypes = new int[]{STATEDGE_ALL, STATEDGE_DIRECT_ALL, type};
+ }
+
+ for (int edgetype : arrtypes) {
+ addEdgeDirectInternal(direction, edge, edgetype);
+ }
+ }
+
+ private void removeEdgeDirectInternal(int direction, StatEdge edge, int edgetype) {
+
+ Map<Integer, List<StatEdge>> mapEdges = direction == DIRECTION_BACKWARD ? mapPredEdges : mapSuccEdges;
+ Map<Integer, List<Statement>> mapStates = direction == DIRECTION_BACKWARD ? mapPredStates : mapSuccStates;
+
+ List<StatEdge> lst = mapEdges.get(edgetype);
+ if (lst != null) {
+ int index = lst.indexOf(edge);
+ if (index >= 0) {
+ lst.remove(index);
+ mapStates.get(edgetype).remove(index);
+ }
+ }
+ }
+
+ private void removeEdgeInternal(int direction, StatEdge edge) {
+
+ int type = edge.getType();
+
+ int[] arrtypes;
+ if (type == StatEdge.TYPE_EXCEPTION) {
+ arrtypes = new int[]{STATEDGE_ALL, StatEdge.TYPE_EXCEPTION};
+ }
+ else {
+ arrtypes = new int[]{STATEDGE_ALL, STATEDGE_DIRECT_ALL, type};
+ }
+
+ for (int edgetype : arrtypes) {
+ removeEdgeDirectInternal(direction, edge, edgetype);
+ }
+ }
+
+ public void addPredecessor(StatEdge edge) {
+ addEdgeInternal(DIRECTION_BACKWARD, edge);
+ }
+
+ public void removePredecessor(StatEdge edge) {
+
+ if (edge == null) { // FIXME: redundant?
+ return;
+ }
+
+ removeEdgeInternal(DIRECTION_BACKWARD, edge);
+ }
+
+ public void addSuccessor(StatEdge edge) {
+ addEdgeInternal(DIRECTION_FORWARD, edge);
+
+ if (edge.closure != null) {
+ edge.closure.getLabelEdges().add(edge);
+ }
+
+ edge.getDestination().addPredecessor(edge);
+ }
+
+ public void removeSuccessor(StatEdge edge) {
+
+ if (edge == null) {
+ return;
+ }
+
+ removeEdgeInternal(DIRECTION_FORWARD, edge);
+
+ if (edge.closure != null) {
+ edge.closure.getLabelEdges().remove(edge);
+ }
+
+ if (edge.getDestination() != null) { // TODO: redundant?
+ edge.getDestination().removePredecessor(edge);
+ }
+ }
+
+ // TODO: make obsolete and remove
+ public void removeAllSuccessors(Statement stat) {
+
+ if (stat == null) {
+ return;
+ }
+
+ for (StatEdge edge : getAllSuccessorEdges()) {
+ if (edge.getDestination() == stat) {
+ removeSuccessor(edge);
+ }
+ }
+ }
+
+ public HashSet<Statement> buildContinueSet() {
+ continueSet.clear();
+
+ for (Statement st : stats) {
+ continueSet.addAll(st.buildContinueSet());
+ if (st != first) {
+ continueSet.remove(st.getBasichead());
+ }
+ }
+
+ for (StatEdge edge : getEdges(StatEdge.TYPE_CONTINUE, DIRECTION_FORWARD)) {
+ continueSet.add(edge.getDestination().getBasichead());
+ }
+
+ if (type == TYPE_DO) {
+ continueSet.remove(first.getBasichead());
+ }
+
+ return continueSet;
+ }
+
+ public void buildMonitorFlags() {
+
+ for (Statement st : stats) {
+ st.buildMonitorFlags();
+ }
+
+ switch (type) {
+ case TYPE_BASICBLOCK:
+ BasicBlockStatement bblock = (BasicBlockStatement)this;
+ InstructionSequence seq = bblock.getBlock().getSeq();
+
+ if (seq != null && seq.length() > 0) {
+ for (int i = 0; i < seq.length(); i++) {
+ if (seq.getInstr(i).opcode == CodeConstants.opc_monitorexit) {
+ containsMonitorExit = true;
+ break;
+ }
+ }
+ isMonitorEnter = (seq.getLastInstr().opcode == CodeConstants.opc_monitorenter);
+ }
+ break;
+ case TYPE_SEQUENCE:
+ case TYPE_IF:
+ containsMonitorExit = false;
+ for (Statement st : stats) {
+ containsMonitorExit |= st.isContainsMonitorExit();
+ }
+
+ break;
+ case TYPE_SYNCRONIZED:
+ case TYPE_ROOT:
+ case TYPE_GENERAL:
+ break;
+ default:
+ containsMonitorExit = false;
+ for (Statement st : stats) {
+ containsMonitorExit |= st.isContainsMonitorExit();
+ }
+ }
+ }
+
+
+ public List<Statement> getReversePostOrderList() {
+ return getReversePostOrderList(first);
+ }
+
+ public List<Statement> getReversePostOrderList(Statement stat) {
+ List<Statement> res = new ArrayList<Statement>();
+
+ addToReversePostOrderListIterative(stat, res);
+
+ return res;
+ }
+
+ public List<Statement> getPostReversePostOrderList() {
+ return getPostReversePostOrderList(null);
+ }
+
+ public List<Statement> getPostReversePostOrderList(List<Statement> lstexits) {
+
+ List<Statement> res = new ArrayList<Statement>();
+
+ if (lstexits == null) {
+ StrongConnectivityHelper schelper = new StrongConnectivityHelper(this);
+ lstexits = StrongConnectivityHelper.getExitReps(schelper.getComponents());
+ }
+
+ HashSet<Statement> setVisited = new HashSet<Statement>();
+
+ for (Statement exit : lstexits) {
+ addToPostReversePostOrderList(exit, res, setVisited);
+ }
+
+ if (res.size() != stats.size()) {
+ DecompilerContext.getLogger().writeMessage("computing post reverse post order failed!", IFernflowerLogger.Severity.ERROR);
+
+ throw new RuntimeException("parsing failure!");
+ }
+
+ return res;
+ }
+
+ public boolean containsStatement(Statement stat) {
+ return this == stat || containsStatementStrict(stat);
+ }
+
+ public boolean containsStatementStrict(Statement stat) {
+
+ if (stats.contains(stat)) {
+ return true;
+ }
+
+ for (int i = 0; i < stats.size(); i++) {
+ if (stats.get(i).containsStatementStrict(stat)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // to be overwritten
+ public String toJava() {
+ return toJava(0);
+ }
+
+ public String toJava(int indent) {
+ throw new RuntimeException("not implemented");
+ }
+
+ // TODO: make obsolete and remove
+ public List<Object> getSequentialObjects() {
+ return new ArrayList<Object>(stats);
+ }
+
+ public void initExprents() {
+ // do nothing
+ }
+
+ public void replaceExprent(Exprent oldexpr, Exprent newexpr) {
+ // do nothing
+ }
+
+ public Statement getSimpleCopy() {
+ throw new RuntimeException("not implemented");
+ }
+
+ public void initSimpleCopy() {
+ if (!stats.isEmpty()) {
+ first = stats.get(0);
+ }
+ }
+
+ public void replaceStatement(Statement oldstat, Statement newstat) {
+
+ for (StatEdge edge : oldstat.getAllPredecessorEdges()) {
+ oldstat.removePredecessor(edge);
+ edge.getSource().changeEdgeNode(DIRECTION_FORWARD, edge, newstat);
+ newstat.addPredecessor(edge);
+ }
+
+ for (StatEdge edge : oldstat.getAllSuccessorEdges()) {
+ oldstat.removeSuccessor(edge);
+ edge.setSource(newstat);
+ newstat.addSuccessor(edge);
+ }
+
+ int statindex = stats.getIndexByKey(oldstat.id);
+ stats.removeWithKey(oldstat.id);
+ stats.addWithKeyAndIndex(statindex, newstat, newstat.id);
+
+ newstat.setParent(this);
+ newstat.post = oldstat.post;
+
+ if (first == oldstat) {
+ first = newstat;
+ }
+
+ List<StatEdge> lst = new ArrayList<StatEdge>(oldstat.getLabelEdges());
+
+ for (int i = lst.size() - 1; i >= 0; i--) {
+ StatEdge edge = lst.get(i);
+ if (edge.getSource() != newstat) {
+ newstat.addLabeledEdge(edge);
+ }
+ else {
+ if (this == edge.getDestination() || this.containsStatementStrict(edge.getDestination())) {
+ edge.closure = null;
+ }
+ else {
+ this.addLabeledEdge(edge);
+ }
+ }
+ }
+
+ oldstat.getLabelEdges().clear();
+ }
+
+
+ // *****************************************************************************
+ // private methods
+ // *****************************************************************************
+
+ private static void addToReversePostOrderListIterative(Statement root, List<Statement> lst) {
+
+ LinkedList<Statement> stackNode = new LinkedList<Statement>();
+ LinkedList<Integer> stackIndex = new LinkedList<Integer>();
+ HashSet<Statement> setVisited = new HashSet<Statement>();
+
+ stackNode.add(root);
+ stackIndex.add(0);
+
+ while (!stackNode.isEmpty()) {
+
+ Statement node = stackNode.getLast();
+ int index = stackIndex.removeLast();
+
+ setVisited.add(node);
+
+ List<StatEdge> lstEdges = node.getAllSuccessorEdges();
+
+ for (; index < lstEdges.size(); index++) {
+ StatEdge edge = lstEdges.get(index);
+ Statement succ = edge.getDestination();
+
+ if (!setVisited.contains(succ) &&
+ (edge.getType() == StatEdge.TYPE_REGULAR || edge.getType() == StatEdge.TYPE_EXCEPTION)) { // TODO: edge filter?
+
+ stackIndex.add(index + 1);
+
+ stackNode.add(succ);
+ stackIndex.add(0);
+
+ break;
+ }
+ }
+
+ if (index == lstEdges.size()) {
+ lst.add(0, node);
+
+ stackNode.removeLast();
+ }
+ }
+ }
+
+
+ private static void addToPostReversePostOrderList(Statement stat, List<Statement> lst, HashSet<Statement> setVisited) {
+
+ if (setVisited.contains(stat)) { // because of not considered exception edges, s. isExitComponent. Should be rewritten, if possible.
+ return;
+ }
+ setVisited.add(stat);
+
+ for (StatEdge prededge : stat.getEdges(StatEdge.TYPE_REGULAR | StatEdge.TYPE_EXCEPTION, DIRECTION_BACKWARD)) {
+ Statement pred = prededge.getSource();
+ if (!setVisited.contains(pred)) {
+ addToPostReversePostOrderList(pred, lst, setVisited);
+ }
+ }
+
+ lst.add(0, stat);
+ }
+
+ // *****************************************************************************
+ // getter and setter methods
+ // *****************************************************************************
+
+ public void changeEdgeNode(int direction, StatEdge edge, Statement value) {
+
+ Map<Integer, List<StatEdge>> mapEdges = direction == DIRECTION_BACKWARD ? mapPredEdges : mapSuccEdges;
+ Map<Integer, List<Statement>> mapStates = direction == DIRECTION_BACKWARD ? mapPredStates : mapSuccStates;
+
+ int type = edge.getType();
+
+ int[] arrtypes;
+ if (type == StatEdge.TYPE_EXCEPTION) {
+ arrtypes = new int[]{STATEDGE_ALL, StatEdge.TYPE_EXCEPTION};
+ }
+ else {
+ arrtypes = new int[]{STATEDGE_ALL, STATEDGE_DIRECT_ALL, type};
+ }
+
+ for (int edgetype : arrtypes) {
+ List<StatEdge> lst = mapEdges.get(edgetype);
+ if (lst != null) {
+ int index = lst.indexOf(edge);
+ if (index >= 0) {
+ mapStates.get(edgetype).set(index, value);
+ }
+ }
+ }
+
+ if (direction == DIRECTION_BACKWARD) {
+ edge.setSource(value);
+ }
+ else {
+ edge.setDestination(value);
+ }
+ }
+
+ public void changeEdgeType(int direction, StatEdge edge, int newtype) {
+
+ int oldtype = edge.getType();
+ if (oldtype == newtype) {
+ return;
+ }
+
+ if (oldtype == StatEdge.TYPE_EXCEPTION || newtype == StatEdge.TYPE_EXCEPTION) {
+ throw new RuntimeException("Invalid edge type!");
+ }
+
+ removeEdgeDirectInternal(direction, edge, oldtype);
+ addEdgeDirectInternal(direction, edge, newtype);
+
+ if (direction == DIRECTION_FORWARD) {
+ edge.getDestination().changeEdgeType(DIRECTION_BACKWARD, edge, newtype);
+ }
+
+ edge.setType(newtype);
+ }
+
+
+ private List<StatEdge> getEdges(int type, int direction) {
+
+ Map<Integer, List<StatEdge>> map = direction == DIRECTION_BACKWARD ? mapPredEdges : mapSuccEdges;
+
+ List<StatEdge> res;
+ if ((type & (type - 1)) == 0) {
+ res = map.get(type);
+ res = res == null ? new ArrayList<StatEdge>() : new ArrayList<StatEdge>(res);
+ }
+ else {
+ res = new ArrayList<StatEdge>();
+ for (int edgetype : StatEdge.TYPES) {
+ if ((type & edgetype) != 0) {
+ List<StatEdge> lst = map.get(edgetype);
+ if (lst != null) {
+ res.addAll(lst);
+ }
+ }
+ }
+ }
+
+ return res;
+ }
+
+ public List<Statement> getNeighbours(int type, int direction) {
+
+ Map<Integer, List<Statement>> map = direction == DIRECTION_BACKWARD ? mapPredStates : mapSuccStates;
+
+ List<Statement> res;
+ if ((type & (type - 1)) == 0) {
+ res = map.get(type);
+ res = res == null ? new ArrayList<Statement>() : new ArrayList<Statement>(res);
+ }
+ else {
+ res = new ArrayList<Statement>();
+ for (int edgetype : StatEdge.TYPES) {
+ if ((type & edgetype) != 0) {
+ List<Statement> lst = map.get(edgetype);
+ if (lst != null) {
+ res.addAll(lst);
+ }
+ }
+ }
+ }
+
+ return res;
+ }
+
+ public Set<Statement> getNeighboursSet(int type, int direction) {
+ return new HashSet<Statement>(getNeighbours(type, direction));
+ }
+
+ public List<StatEdge> getSuccessorEdges(int type) {
+ return getEdges(type, DIRECTION_FORWARD);
+ }
+
+ public List<StatEdge> getPredecessorEdges(int type) {
+ return getEdges(type, DIRECTION_BACKWARD);
+ }
+
+ public List<StatEdge> getAllSuccessorEdges() {
+ return getEdges(STATEDGE_ALL, DIRECTION_FORWARD);
+ }
+
+ public List<StatEdge> getAllPredecessorEdges() {
+ return getEdges(STATEDGE_ALL, DIRECTION_BACKWARD);
+ }
+
+ public Statement getFirst() {
+ return first;
+ }
+
+ public void setFirst(Statement first) {
+ this.first = first;
+ }
+
+ public Statement getPost() {
+ return post;
+ }
+
+ public void setPost(Statement post) {
+ this.post = post;
+ }
+
+ public VBStyleCollection<Statement, Integer> getStats() {
+ return stats;
+ }
+
+ public int getLastBasicType() {
+ return lastBasicType;
+ }
+
+ public HashSet<Statement> getContinueSet() {
+ return continueSet;
+ }
+
+ public boolean isContainsMonitorExit() {
+ return containsMonitorExit;
+ }
+
+ public boolean isMonitorEnter() {
+ return isMonitorEnter;
+ }
+
+ public BasicBlockStatement getBasichead() {
+ if (type == TYPE_BASICBLOCK) {
+ return (BasicBlockStatement)this;
+ }
+ else {
+ return first.getBasichead();
+ }
+ }
+
+ public boolean isLabeled() {
+
+ for (StatEdge edge : labelEdges) {
+ if (edge.labeled && edge.explicit) { // FIXME: consistent setting
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean hasBasicSuccEdge() {
+
+ // FIXME: default switch
+
+ return type == TYPE_BASICBLOCK || (type == TYPE_IF &&
+ ((IfStatement)this).iftype == IfStatement.IFTYPE_IF) ||
+ (type == TYPE_DO && ((DoStatement)this).getLooptype() != DoStatement.LOOP_DO);
+ }
+
+
+ public Statement getParent() {
+ return parent;
+ }
+
+ public void setParent(Statement parent) {
+ this.parent = parent;
+ }
+
+ public HashSet<StatEdge> getLabelEdges() { // FIXME: why HashSet?
+ return labelEdges;
+ }
+
+ public List<Exprent> getVarDefinitions() {
+ return varDefinitions;
+ }
+
+ public List<Exprent> getExprents() {
+ return exprents;
+ }
+
+ public void setExprents(List<Exprent> exprents) {
+ this.exprents = exprents;
+ }
+
+ public boolean isCopied() {
+ return copied;
+ }
+
+ public void setCopied(boolean copied) {
+ this.copied = copied;
+ }
+
+ // helper methods
+ public String toString() {
+ return id.toString();
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java
new file mode 100644
index 000000000000..b54642dfb58f
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java
@@ -0,0 +1,367 @@
+/*
+ * 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.modules.decompiler.stats;
+
+import org.jetbrains.java.decompiler.code.SwitchInstruction;
+import org.jetbrains.java.decompiler.code.cfg.BasicBlock;
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
+import org.jetbrains.java.decompiler.modules.decompiler.DecHelper;
+import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
+import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.SwitchExprent;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+
+import java.util.*;
+
+public class SwitchStatement extends Statement {
+
+ // *****************************************************************************
+ // private fields
+ // *****************************************************************************
+
+ private List<Statement> caseStatements = new ArrayList<Statement>();
+
+ private List<List<StatEdge>> caseEdges = new ArrayList<List<StatEdge>>();
+
+ private List<List<ConstExprent>> caseValues = new ArrayList<List<ConstExprent>>();
+
+ private StatEdge default_edge;
+
+ private List<Exprent> headexprent = new ArrayList<Exprent>();
+
+ // *****************************************************************************
+ // constructors
+ // *****************************************************************************
+
+ private SwitchStatement() {
+ type = TYPE_SWITCH;
+
+ headexprent.add(null);
+ }
+
+ private SwitchStatement(Statement head, Statement poststat) {
+
+ this();
+
+ first = head;
+ stats.addWithKey(head, head.id);
+
+ // find post node
+ Set<Statement> lstNodes = new HashSet<Statement>(head.getNeighbours(StatEdge.TYPE_REGULAR, DIRECTION_FORWARD));
+
+ // cluster nodes
+ if (poststat != null) {
+ post = poststat;
+ lstNodes.remove(post);
+ }
+
+ default_edge = head.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL).get(0);
+
+ for (Statement st : lstNodes) {
+ stats.addWithKey(st, st.id);
+ }
+ }
+
+ // *****************************************************************************
+ // public methods
+ // *****************************************************************************
+
+ public static Statement isHead(Statement head) {
+
+ if (head.type == Statement.TYPE_BASICBLOCK && head.getLastBasicType() == Statement.LASTBASICTYPE_SWITCH) {
+
+ List<Statement> lst = new ArrayList<Statement>();
+ if (DecHelper.isChoiceStatement(head, lst)) {
+ Statement post = lst.remove(0);
+
+ for (Statement st : lst) {
+ if (st.isMonitorEnter()) {
+ return null;
+ }
+ }
+
+ if (DecHelper.checkStatementExceptions(lst)) {
+ return new SwitchStatement(head, post);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public String toJava(int indent) {
+
+ String indstr = InterpreterUtil.getIndentString(indent);
+
+ String new_line_separator = DecompilerContext.getNewLineSeparator();
+
+ StringBuilder buf = new StringBuilder();
+ buf.append(ExprProcessor.listToJava(varDefinitions, indent));
+ buf.append(first.toJava(indent));
+
+ if (isLabeled()) {
+ buf.append(indstr).append("label").append(this.id).append(":").append(new_line_separator);
+ }
+
+ buf.append(indstr).append(headexprent.get(0).toJava(indent)).append(" {").append(new_line_separator);
+
+ VarType switch_type = headexprent.get(0).getExprType();
+
+ for (int i = 0; i < caseStatements.size(); i++) {
+
+ Statement stat = caseStatements.get(i);
+ List<StatEdge> edges = caseEdges.get(i);
+ List<ConstExprent> values = caseValues.get(i);
+
+ for (int j = 0; j < edges.size(); j++) {
+ if (edges.get(j) == default_edge) {
+ buf.append(indstr).append("default:").append(new_line_separator);
+ }
+ else {
+ ConstExprent value = (ConstExprent)values.get(j).copy();
+ value.setConsttype(switch_type);
+
+ buf.append(indstr).append("case ").append(value.toJava(indent)).append(":").append(new_line_separator);
+ }
+ }
+
+ buf.append(ExprProcessor.jmpWrapper(stat, indent + 1, false));
+ }
+
+ buf.append(indstr).append("}").append(new_line_separator);
+
+ return buf.toString();
+ }
+
+ public void initExprents() {
+ SwitchExprent swexpr = (SwitchExprent)first.getExprents().remove(first.getExprents().size() - 1);
+ swexpr.setCaseValues(caseValues);
+
+ headexprent.set(0, swexpr);
+ }
+
+ public List<Object> getSequentialObjects() {
+
+ List<Object> lst = new ArrayList<Object>(stats);
+ lst.add(1, headexprent.get(0));
+
+ return lst;
+ }
+
+ public void replaceExprent(Exprent oldexpr, Exprent newexpr) {
+ if (headexprent.get(0) == oldexpr) {
+ headexprent.set(0, newexpr);
+ }
+ }
+
+ public void replaceStatement(Statement oldstat, Statement newstat) {
+
+ for (int i = 0; i < caseStatements.size(); i++) {
+ if (caseStatements.get(i) == oldstat) {
+ caseStatements.set(i, newstat);
+ }
+ }
+
+ super.replaceStatement(oldstat, newstat);
+ }
+
+ public Statement getSimpleCopy() {
+ return new SwitchStatement();
+ }
+
+ public void initSimpleCopy() {
+ first = stats.get(0);
+ default_edge = first.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL).get(0);
+
+ sortEdgesAndNodes();
+ }
+
+ // *****************************************************************************
+ // private methods
+ // *****************************************************************************
+
+ public void sortEdgesAndNodes() {
+
+ HashMap<StatEdge, Integer> mapEdgeIndex = new HashMap<StatEdge, Integer>();
+
+ List<StatEdge> lstFirstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL);
+ for (int i = 0; i < lstFirstSuccs.size(); i++) {
+ mapEdgeIndex.put(lstFirstSuccs.get(i), i == 0 ? lstFirstSuccs.size() : i);
+ }
+
+ // case values
+ BasicBlockStatement bbstat = (BasicBlockStatement)first;
+ int[] values = ((SwitchInstruction)bbstat.getBlock().getLastInstruction()).getValues();
+
+ List<Statement> nodes = new ArrayList<Statement>();
+ List<List<Integer>> edges = new ArrayList<List<Integer>>();
+
+ // collect regular edges
+ for (int i = 1; i < stats.size(); i++) {
+
+ Statement stat = stats.get(i);
+
+ List<Integer> lst = new ArrayList<Integer>();
+ for (StatEdge edge : stat.getPredecessorEdges(StatEdge.TYPE_REGULAR)) {
+ if (edge.getSource() == first) {
+ lst.add(mapEdgeIndex.get(edge));
+ }
+ }
+ Collections.sort(lst);
+
+ nodes.add(stat);
+ edges.add(lst);
+ }
+
+ // collect exit edges
+ List<StatEdge> lstExitEdges = first.getSuccessorEdges(StatEdge.TYPE_BREAK | StatEdge.TYPE_CONTINUE);
+ while (!lstExitEdges.isEmpty()) {
+ StatEdge edge = lstExitEdges.get(0);
+
+ List<Integer> lst = new ArrayList<Integer>();
+ for (int i = lstExitEdges.size() - 1; i >= 0; i--) {
+ StatEdge edgeTemp = lstExitEdges.get(i);
+ if (edgeTemp.getDestination() == edge.getDestination() && edgeTemp.getType() == edge.getType()) {
+ lst.add(mapEdgeIndex.get(edgeTemp));
+ lstExitEdges.remove(i);
+ }
+ }
+ Collections.sort(lst);
+
+ nodes.add(null);
+ edges.add(lst);
+ }
+
+ // sort edges (bubblesort)
+ for (int i = 0; i < edges.size() - 1; i++) {
+ for (int j = edges.size() - 1; j > i; j--) {
+ if (edges.get(j - 1).get(0) > edges.get(j).get(0)) {
+ edges.set(j, edges.set(j - 1, edges.get(j)));
+ nodes.set(j, nodes.set(j - 1, nodes.get(j)));
+ }
+ }
+ }
+
+ // sort statement cliques
+ for (int index = 0; index < nodes.size(); index++) {
+ Statement stat = nodes.get(index);
+
+ if (stat != null) {
+ HashSet<Statement> setPreds = new HashSet<Statement>(stat.getNeighbours(StatEdge.TYPE_REGULAR, DIRECTION_BACKWARD));
+ setPreds.remove(first);
+
+ if (!setPreds.isEmpty()) {
+ Statement pred =
+ setPreds.iterator().next(); // assumption: at most one predecessor node besides the head. May not hold true for obfuscated code.
+ for (int j = 0; j < nodes.size(); j++) {
+ if (j != (index - 1) && nodes.get(j) == pred) {
+ nodes.add(j + 1, stat);
+ edges.add(j + 1, edges.get(index));
+
+ if (j > index) {
+ nodes.remove(index);
+ edges.remove(index);
+ index--;
+ }
+ else {
+ nodes.remove(index + 1);
+ edges.remove(index + 1);
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // translate indices back into edges
+ List<List<StatEdge>> lstEdges = new ArrayList<List<StatEdge>>();
+ List<List<ConstExprent>> lstValues = new ArrayList<List<ConstExprent>>();
+
+ for (List<Integer> lst : edges) {
+ List<StatEdge> lste = new ArrayList<StatEdge>();
+ List<ConstExprent> lstv = new ArrayList<ConstExprent>();
+
+ List<StatEdge> lstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL);
+ for (Integer in : lst) {
+ int index = in == lstSuccs.size() ? 0 : in;
+
+ lste.add(lstSuccs.get(index));
+ lstv.add(index == 0 ? null : new ConstExprent(values[index - 1], false));
+ }
+ lstEdges.add(lste);
+ lstValues.add(lstv);
+ }
+
+ // replace null statements with dummy basic blocks
+ for (int i = 0; i < nodes.size(); i++) {
+ if (nodes.get(i) == null) {
+ BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock(
+ DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)));
+
+ StatEdge sample_edge = lstEdges.get(i).get(0);
+
+ bstat.addSuccessor(new StatEdge(sample_edge.getType(), bstat, sample_edge.getDestination(), sample_edge.closure));
+
+ for (StatEdge edge : lstEdges.get(i)) {
+
+ edge.getSource().changeEdgeType(DIRECTION_FORWARD, edge, StatEdge.TYPE_REGULAR);
+ edge.closure.getLabelEdges().remove(edge);
+
+ edge.getDestination().removePredecessor(edge);
+ edge.getSource().changeEdgeNode(DIRECTION_FORWARD, edge, bstat);
+ bstat.addPredecessor(edge);
+ }
+
+ nodes.set(i, bstat);
+ stats.addWithKey(bstat, bstat.id);
+ bstat.setParent(this);
+ }
+ }
+
+ caseStatements = nodes;
+ caseEdges = lstEdges;
+ caseValues = lstValues;
+ }
+
+ public List<Exprent> getHeadexprentList() {
+ return headexprent;
+ }
+
+ public Exprent getHeadexprent() {
+ return headexprent.get(0);
+ }
+
+ public List<List<StatEdge>> getCaseEdges() {
+ return caseEdges;
+ }
+
+ public List<Statement> getCaseStatements() {
+ return caseStatements;
+ }
+
+ public StatEdge getDefault_edge() {
+ return default_edge;
+ }
+
+ public List<List<ConstExprent>> getCaseValues() {
+ return caseValues;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SynchronizedStatement.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SynchronizedStatement.java
new file mode 100644
index 000000000000..6e3dc4704175
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SynchronizedStatement.java
@@ -0,0 +1,153 @@
+/*
+ * 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.modules.decompiler.stats;
+
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
+import org.jetbrains.java.decompiler.modules.decompiler.SequenceHelper;
+import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class SynchronizedStatement extends Statement {
+
+ private Statement body;
+
+ private List<Exprent> headexprent = new ArrayList<Exprent>();
+
+ // *****************************************************************************
+ // constructors
+ // *****************************************************************************
+
+ public SynchronizedStatement() {
+ type = TYPE_SYNCRONIZED;
+
+ headexprent.add(null);
+ }
+
+ public SynchronizedStatement(Statement head, Statement body, Statement exc) {
+
+ this();
+
+ first = head;
+ stats.addWithKey(head, head.id);
+
+ this.body = body;
+ stats.addWithKey(body, body.id);
+
+ stats.addWithKey(exc, exc.id);
+
+ List<StatEdge> lstSuccs = body.getSuccessorEdges(STATEDGE_DIRECT_ALL);
+ if (!lstSuccs.isEmpty()) {
+ StatEdge edge = lstSuccs.get(0);
+ if (edge.getType() == StatEdge.TYPE_REGULAR) {
+ post = edge.getDestination();
+ }
+ }
+ }
+
+
+ // *****************************************************************************
+ // public methods
+ // *****************************************************************************
+
+ public String toJava(int indent) {
+ String indstr = InterpreterUtil.getIndentString(indent);
+
+ String new_line_separator = DecompilerContext.getNewLineSeparator();
+
+ StringBuilder buf = new StringBuilder();
+ buf.append(ExprProcessor.listToJava(varDefinitions, indent));
+ buf.append(first.toJava(indent));
+
+ if (isLabeled()) {
+ buf.append(indstr).append("label").append(this.id).append(":").append(new_line_separator);
+ }
+
+ buf.append(indstr).append(headexprent.get(0).toJava(indent)).append(" {").append(new_line_separator);
+ buf.append(ExprProcessor.jmpWrapper(body, indent + 1, true));
+ buf.append(indstr).append("}").append(new_line_separator);
+
+ return buf.toString();
+ }
+
+ public void initExprents() {
+ headexprent.set(0, first.getExprents().remove(first.getExprents().size() - 1));
+ }
+
+ public List<Object> getSequentialObjects() {
+
+ List<Object> lst = new ArrayList<Object>(stats);
+ lst.add(1, headexprent.get(0));
+
+ return lst;
+ }
+
+ public void replaceExprent(Exprent oldexpr, Exprent newexpr) {
+ if (headexprent.get(0) == oldexpr) {
+ headexprent.set(0, newexpr);
+ }
+ }
+
+ public void replaceStatement(Statement oldstat, Statement newstat) {
+
+ if (body == oldstat) {
+ body = newstat;
+ }
+
+ super.replaceStatement(oldstat, newstat);
+ }
+
+ public void removeExc() {
+ Statement exc = stats.get(2);
+ SequenceHelper.destroyStatementContent(exc, true);
+
+ stats.removeWithKey(exc.id);
+ }
+
+ public Statement getSimpleCopy() {
+ return new SynchronizedStatement();
+ }
+
+ public void initSimpleCopy() {
+ first = stats.get(0);
+ body = stats.get(1);
+ }
+
+ // *****************************************************************************
+ // getter and setter methods
+ // *****************************************************************************
+
+ public Statement getBody() {
+ return body;
+ }
+
+ public void setBody(Statement body) {
+ this.body = body;
+ }
+
+ public List<Exprent> getHeadexprentList() {
+ return headexprent;
+ }
+
+ public Exprent getHeadexprent() {
+ return headexprent.get(0);
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/CheckTypesResult.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/CheckTypesResult.java
new file mode 100644
index 000000000000..08c32ec398f9
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/CheckTypesResult.java
@@ -0,0 +1,57 @@
+/*
+ * 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.modules.decompiler.vars;
+
+import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CheckTypesResult {
+
+ private List<ExprentTypePair> lstMaxTypeExprents = new ArrayList<ExprentTypePair>();
+
+ private List<ExprentTypePair> lstMinTypeExprents = new ArrayList<ExprentTypePair>();
+
+ public void addMaxTypeExprent(Exprent exprent, VarType type) {
+ lstMaxTypeExprents.add(new ExprentTypePair(exprent, type, null));
+ }
+
+ public void addMinTypeExprent(Exprent exprent, VarType type) {
+ lstMinTypeExprents.add(new ExprentTypePair(exprent, type, null));
+ }
+
+ public List<ExprentTypePair> getLstMaxTypeExprents() {
+ return lstMaxTypeExprents;
+ }
+
+ public List<ExprentTypePair> getLstMinTypeExprents() {
+ return lstMinTypeExprents;
+ }
+
+ public static class ExprentTypePair {
+ public Exprent exprent;
+ public VarType type;
+ public VarType desttype;
+
+ public ExprentTypePair(Exprent exprent, VarType type, VarType desttype) {
+ this.exprent = exprent;
+ this.type = type;
+ this.desttype = desttype;
+ }
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java
new file mode 100644
index 000000000000..fdd6cd5015f8
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java
@@ -0,0 +1,355 @@
+/*
+ * 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.modules.decompiler.vars;
+
+import org.jetbrains.java.decompiler.code.CodeConstants;
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
+import org.jetbrains.java.decompiler.struct.StructClass;
+import org.jetbrains.java.decompiler.struct.StructMethod;
+import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+public class VarDefinitionHelper {
+
+ private HashMap<Integer, Statement> mapVarDefStatements;
+
+ // statement.id, defined vars
+ private HashMap<Integer, HashSet<Integer>> mapStatementVars;
+
+ private HashSet<Integer> implDefVars;
+
+ private VarProcessor varproc;
+
+ public VarDefinitionHelper(Statement root, StructMethod mt, VarProcessor varproc) {
+
+ mapVarDefStatements = new HashMap<Integer, Statement>();
+ mapStatementVars = new HashMap<Integer, HashSet<Integer>>();
+ implDefVars = new HashSet<Integer>();
+
+ this.varproc = varproc;
+
+ VarNamesCollector vc = DecompilerContext.getVarNamesCollector();
+
+ boolean thisvar = !mt.hasModifier(CodeConstants.ACC_STATIC);
+
+ MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
+
+ int paramcount = 0;
+ if (thisvar) {
+ paramcount = 1;
+ }
+ paramcount += md.params.length;
+
+
+ // method parameters are implicitly defined
+ int varindex = 0;
+ for (int i = 0; i < paramcount; i++) {
+ implDefVars.add(varindex);
+ varproc.setVarName(new VarVersionPaar(varindex, 0), vc.getFreeName(varindex));
+
+ if (thisvar) {
+ if (i == 0) {
+ varindex++;
+ }
+ else {
+ varindex += md.params[i - 1].stack_size;
+ }
+ }
+ else {
+ varindex += md.params[i].stack_size;
+ }
+ }
+
+ if (thisvar) {
+ StructClass current_class = (StructClass)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS);
+
+ varproc.getThisvars().put(new VarVersionPaar(0, 0), current_class.qualifiedName);
+ varproc.setVarName(new VarVersionPaar(0, 0), "this");
+ vc.addName("this");
+ }
+
+ // catch variables are implicitly defined
+ LinkedList<Statement> stack = new LinkedList<Statement>();
+ stack.add(root);
+
+ while (!stack.isEmpty()) {
+ Statement st = stack.removeFirst();
+
+ List<VarExprent> lstVars = null;
+ if (st.type == Statement.TYPE_CATCHALL) {
+ lstVars = ((CatchAllStatement)st).getVars();
+ }
+ else if (st.type == Statement.TYPE_TRYCATCH) {
+ lstVars = ((CatchStatement)st).getVars();
+ }
+
+ if (lstVars != null) {
+ for (VarExprent var : lstVars) {
+ implDefVars.add(var.getIndex());
+ varproc.setVarName(new VarVersionPaar(var), vc.getFreeName(var.getIndex()));
+ var.setDefinition(true);
+ }
+ }
+
+ stack.addAll(st.getStats());
+ }
+
+ initStatement(root);
+ }
+
+
+ public void setVarDefinitions() {
+
+ VarNamesCollector vc = DecompilerContext.getVarNamesCollector();
+
+ for (Entry<Integer, Statement> en : mapVarDefStatements.entrySet()) {
+ Statement stat = en.getValue();
+ Integer index = en.getKey();
+
+ if (implDefVars.contains(index)) {
+ // already implicitly defined
+ continue;
+ }
+
+ varproc.setVarName(new VarVersionPaar(index.intValue(), 0), vc.getFreeName(index));
+
+ // special case for
+ if (stat.type == Statement.TYPE_DO) {
+ DoStatement dstat = (DoStatement)stat;
+ if (dstat.getLooptype() == DoStatement.LOOP_FOR) {
+
+ if (dstat.getInitExprent() != null && setDefinition(dstat.getInitExprent(), index)) {
+ continue;
+ }
+ else {
+ List<Exprent> lstSpecial = Arrays.asList(dstat.getConditionExprent(), dstat.getIncExprent());
+ for (VarExprent var : getAllVars(lstSpecial)) {
+ if (var.getIndex() == index.intValue()) {
+ stat = stat.getParent();
+ break;
+ }
+ }
+ }
+ }
+ }
+
+
+ Statement first = findFirstBlock(stat, index);
+
+ List<Exprent> lst;
+ if (first == null) {
+ lst = stat.getVarDefinitions();
+ }
+ else if (first.getExprents() == null) {
+ lst = first.getVarDefinitions();
+ }
+ else {
+ lst = first.getExprents();
+ }
+
+
+ boolean defset = false;
+
+ // search for the first assignement to var [index]
+ int addindex = 0;
+ for (Exprent expr : lst) {
+ if (setDefinition(expr, index)) {
+ defset = true;
+ break;
+ }
+ else {
+ boolean foundvar = false;
+ for (Exprent exp : expr.getAllExprents(true)) {
+ if (exp.type == Exprent.EXPRENT_VAR && ((VarExprent)exp).getIndex() == index) {
+ foundvar = true;
+ break;
+ }
+ }
+ if (foundvar) {
+ break;
+ }
+ }
+ addindex++;
+ }
+
+ if (!defset) {
+ VarExprent var = new VarExprent(index.intValue(), varproc.getVarType(new VarVersionPaar(index.intValue(), 0)), varproc);
+ var.setDefinition(true);
+
+ lst.add(addindex, var);
+ }
+ }
+ }
+
+
+ // *****************************************************************************
+ // private methods
+ // *****************************************************************************
+
+ private Statement findFirstBlock(Statement stat, Integer varindex) {
+
+ LinkedList<Statement> stack = new LinkedList<Statement>();
+ stack.add(stat);
+
+ while (!stack.isEmpty()) {
+ Statement st = stack.remove(0);
+
+ if (stack.isEmpty() || mapStatementVars.get(st.id).contains(varindex)) {
+
+ if (st.isLabeled() && !stack.isEmpty()) {
+ return st;
+ }
+
+ if (st.getExprents() != null) {
+ return st;
+ }
+ else {
+ stack.clear();
+
+ switch (st.type) {
+ case Statement.TYPE_SEQUENCE:
+ stack.addAll(0, st.getStats());
+ break;
+ case Statement.TYPE_IF:
+ case Statement.TYPE_ROOT:
+ case Statement.TYPE_SWITCH:
+ case Statement.TYPE_SYNCRONIZED:
+ stack.add(st.getFirst());
+ break;
+ default:
+ return st;
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private Set<Integer> initStatement(Statement stat) {
+
+ HashMap<Integer, Integer> mapCount = new HashMap<Integer, Integer>();
+
+ List<VarExprent> condlst;
+
+ if (stat.getExprents() == null) {
+
+ // recurse on children statements
+ List<Integer> childVars = new ArrayList<Integer>();
+ List<Exprent> currVars = new ArrayList<Exprent>();
+
+ for (Object obj : stat.getSequentialObjects()) {
+ if (obj instanceof Statement) {
+ Statement st = (Statement)obj;
+ childVars.addAll(initStatement(st));
+
+ if (st.type == DoStatement.TYPE_DO) {
+ DoStatement dost = (DoStatement)st;
+ if (dost.getLooptype() != DoStatement.LOOP_FOR &&
+ dost.getLooptype() != DoStatement.LOOP_DO) {
+ currVars.add(dost.getConditionExprent());
+ }
+ }
+ else if (st.type == DoStatement.TYPE_CATCHALL) {
+ CatchAllStatement fin = (CatchAllStatement)st;
+ if (fin.isFinally() && fin.getMonitor() != null) {
+ currVars.add(fin.getMonitor());
+ }
+ }
+ }
+ else if (obj instanceof Exprent) {
+ currVars.add((Exprent)obj);
+ }
+ }
+
+ // children statements
+ for (Integer index : childVars) {
+ Integer count = mapCount.get(index);
+ if (count == null) {
+ count = new Integer(0);
+ }
+ mapCount.put(index, new Integer(count.intValue() + 1));
+ }
+
+ condlst = getAllVars(currVars);
+ }
+ else {
+ condlst = getAllVars(stat.getExprents());
+ }
+
+ // this statement
+ for (VarExprent var : condlst) {
+ mapCount.put(new Integer(var.getIndex()), new Integer(2));
+ }
+
+
+ HashSet<Integer> set = new HashSet<Integer>(mapCount.keySet());
+
+ // put all variables defined in this statement into the set
+ for (Entry<Integer, Integer> en : mapCount.entrySet()) {
+ if (en.getValue().intValue() > 1) {
+ mapVarDefStatements.put(en.getKey(), stat);
+ }
+ }
+
+ mapStatementVars.put(stat.id, set);
+
+ return set;
+ }
+
+ private static List<VarExprent> getAllVars(List<Exprent> lst) {
+
+ List<VarExprent> res = new ArrayList<VarExprent>();
+ List<Exprent> listTemp = new ArrayList<Exprent>();
+
+ for (Exprent expr : lst) {
+ listTemp.addAll(expr.getAllExprents(true));
+ listTemp.add(expr);
+ }
+
+ for (Exprent exprent : listTemp) {
+ if (exprent.type == Exprent.EXPRENT_VAR) {
+ res.add((VarExprent)exprent);
+ }
+ }
+
+ return res;
+ }
+
+ private static boolean setDefinition(Exprent expr, Integer index) {
+ if (expr.type == Exprent.EXPRENT_ASSIGNMENT) {
+ Exprent left = ((AssignmentExprent)expr).getLeft();
+ if (left.type == Exprent.EXPRENT_VAR) {
+ VarExprent var = (VarExprent)left;
+ if (var.getIndex() == index.intValue()) {
+ var.setDefinition(true);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java
new file mode 100644
index 000000000000..266ba94e1cda
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java
@@ -0,0 +1,129 @@
+/*
+ * 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.modules.decompiler.vars;
+
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
+import org.jetbrains.java.decompiler.struct.StructMethod;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+
+public class VarProcessor {
+
+ private HashMap<VarVersionPaar, String> mapVarNames = new HashMap<VarVersionPaar, String>();
+
+ private VarVersionsProcessor varvers;
+
+ private HashMap<VarVersionPaar, String> thisvars = new HashMap<VarVersionPaar, String>();
+
+ private HashSet<VarVersionPaar> externvars = new HashSet<VarVersionPaar>();
+
+ public void setVarVersions(RootStatement root) {
+
+ varvers = new VarVersionsProcessor();
+ varvers.setVarVersions(root);
+ }
+
+ public void setVarDefinitions(Statement root) {
+ mapVarNames = new HashMap<VarVersionPaar, String>();
+
+ VarDefinitionHelper defproc = new VarDefinitionHelper(root,
+ (StructMethod)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD),
+ this);
+ defproc.setVarDefinitions();
+ }
+
+ public void setDebugVarNames(Map<Integer, String> mapDebugVarNames) {
+ if (varvers == null) {
+ return;
+ }
+
+ HashMap<Integer, Integer> mapOriginalVarIndices = varvers.getMapOriginalVarIndices();
+
+ List<VarVersionPaar> listVars = new ArrayList<VarVersionPaar>(mapVarNames.keySet());
+ Collections.sort(listVars, new Comparator<VarVersionPaar>() {
+ public int compare(VarVersionPaar o1, VarVersionPaar o2) {
+ return o1.var > o2.var ? 1 : (o1.var == o2.var ? 0 : -1);
+ }
+ });
+
+ HashMap<String, Integer> mapNames = new HashMap<String, Integer>();
+
+ for (VarVersionPaar varpaar : listVars) {
+ String name = mapVarNames.get(varpaar);
+
+ Integer orindex = mapOriginalVarIndices.get(varpaar.var);
+ if (orindex != null && mapDebugVarNames.containsKey(orindex)) {
+ name = mapDebugVarNames.get(orindex);
+ }
+
+ Integer counter = mapNames.get(name);
+ mapNames.put(name, counter == null ? counter = new Integer(0) : ++counter);
+
+ if (counter > 0) {
+ name += String.valueOf(counter);
+ }
+
+ mapVarNames.put(varpaar, name);
+ }
+ }
+
+ public void refreshVarNames(VarNamesCollector vc) {
+
+ HashMap<VarVersionPaar, String> tempVarNames = new HashMap<VarVersionPaar, String>(mapVarNames);
+ for (Entry<VarVersionPaar, String> ent : tempVarNames.entrySet()) {
+ mapVarNames.put(ent.getKey(), vc.getFreeName(ent.getValue()));
+ }
+ }
+
+
+ public VarType getVarType(VarVersionPaar varpaar) {
+ return varvers == null ? null : varvers.getVarType(varpaar);
+ }
+
+ public void setVarType(VarVersionPaar varpaar, VarType type) {
+ varvers.setVarType(varpaar, type);
+ }
+
+ public String getVarName(VarVersionPaar varpaar) {
+ return mapVarNames == null ? null : mapVarNames.get(varpaar);
+ }
+
+ public void setVarName(VarVersionPaar varpaar, String name) {
+ mapVarNames.put(varpaar, name);
+ }
+
+ public int getVarFinal(VarVersionPaar varpaar) {
+ return varvers == null ? VarTypeProcessor.VAR_FINAL : varvers.getVarFinal(varpaar);
+ }
+
+ public void setVarFinal(VarVersionPaar varpaar, int finaltype) {
+ varvers.setVarFinal(varpaar, finaltype);
+ }
+
+ public HashMap<VarVersionPaar, String> getThisvars() {
+ return thisvars;
+ }
+
+ public HashSet<VarVersionPaar> getExternvars() {
+ return externvars;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java
new file mode 100644
index 000000000000..a21d71f7e9f7
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java
@@ -0,0 +1,278 @@
+/*
+ * 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.modules.decompiler.vars;
+
+import org.jetbrains.java.decompiler.code.CodeConstants;
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
+import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
+import org.jetbrains.java.decompiler.struct.StructClass;
+import org.jetbrains.java.decompiler.struct.StructMethod;
+import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+
+public class VarTypeProcessor {
+
+ public static final int VAR_NONFINAL = 1;
+ public static final int VAR_FINALEXPLICIT = 2;
+ public static final int VAR_FINAL = 3;
+
+ private HashMap<VarVersionPaar, VarType> mapExprentMinTypes = new HashMap<VarVersionPaar, VarType>();
+
+ private HashMap<VarVersionPaar, VarType> mapExprentMaxTypes = new HashMap<VarVersionPaar, VarType>();
+
+ private HashMap<VarVersionPaar, Integer> mapFinalVars = new HashMap<VarVersionPaar, Integer>();
+
+ private void setInitVars(RootStatement root) {
+
+ StructMethod mt = (StructMethod)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD);
+
+ // method descriptor
+ boolean thisvar = !mt.hasModifier(CodeConstants.ACC_STATIC);
+
+ MethodDescriptor md = (MethodDescriptor)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR);
+
+ if (thisvar) {
+ VarType cltype = new VarType(CodeConstants.TYPE_OBJECT, 0,
+ ((StructClass)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS)).qualifiedName);
+ mapExprentMinTypes.put(new VarVersionPaar(0, 1), cltype);
+ mapExprentMaxTypes.put(new VarVersionPaar(0, 1), cltype);
+ }
+
+ int varindex = 0;
+ for (int i = 0; i < md.params.length; i++) {
+ mapExprentMinTypes.put(new VarVersionPaar(varindex + (thisvar ? 1 : 0), 1), md.params[i]);
+ mapExprentMaxTypes.put(new VarVersionPaar(varindex + (thisvar ? 1 : 0), 1), md.params[i]);
+ varindex += md.params[i].stack_size;
+ }
+
+ // catch variables
+ LinkedList<Statement> stack = new LinkedList<Statement>();
+ stack.add(root);
+
+ while (!stack.isEmpty()) {
+ Statement stat = stack.removeFirst();
+
+ List<VarExprent> lstVars = null;
+ if (stat.type == Statement.TYPE_CATCHALL) {
+ lstVars = ((CatchAllStatement)stat).getVars();
+ }
+ else if (stat.type == Statement.TYPE_TRYCATCH) {
+ lstVars = ((CatchStatement)stat).getVars();
+ }
+
+ if (lstVars != null) {
+ for (VarExprent var : lstVars) {
+ mapExprentMinTypes.put(new VarVersionPaar(var.getIndex(), 1), var.getVartype());
+ mapExprentMaxTypes.put(new VarVersionPaar(var.getIndex(), 1), var.getVartype());
+ }
+ }
+
+ stack.addAll(stat.getStats());
+ }
+ }
+
+ public void calculateVarTypes(RootStatement root, DirectGraph dgraph) {
+
+ setInitVars(root);
+
+ resetExprentTypes(dgraph);
+
+ while (!processVarTypes(dgraph)) ;
+ }
+
+ private static void resetExprentTypes(DirectGraph dgraph) {
+
+ dgraph.iterateExprents(new DirectGraph.ExprentIterator() {
+ public int processExprent(Exprent exprent) {
+ List<Exprent> lst = exprent.getAllExprents(true);
+ lst.add(exprent);
+
+ for (Exprent expr : lst) {
+ if (expr.type == Exprent.EXPRENT_VAR) {
+ ((VarExprent)expr).setVartype(VarType.VARTYPE_UNKNOWN);
+ }
+ else if (expr.type == Exprent.EXPRENT_CONST) {
+ ConstExprent cexpr = (ConstExprent)expr;
+ if (cexpr.getConsttype().type_family == CodeConstants.TYPE_FAMILY_INTEGER) {
+ cexpr.setConsttype(new ConstExprent(cexpr.getIntValue(), cexpr.isBoolPermitted()).getConsttype());
+ }
+ }
+ }
+ return 0;
+ }
+ });
+ }
+
+ private boolean processVarTypes(DirectGraph dgraph) {
+
+ return dgraph.iterateExprents(new DirectGraph.ExprentIterator() {
+ public int processExprent(Exprent exprent) {
+ return checkTypeExprent(exprent) ? 0 : 1;
+ }
+ });
+ }
+
+
+ private boolean checkTypeExprent(Exprent exprent) {
+
+ for (Exprent expr : exprent.getAllExprents()) {
+ if (!checkTypeExprent(expr)) {
+ return false;
+ }
+ }
+
+ if (exprent.type == Exprent.EXPRENT_CONST) {
+ ConstExprent cexpr = (ConstExprent)exprent;
+ if (cexpr.getConsttype().type_family <= CodeConstants.TYPE_FAMILY_INTEGER) { // boolean or integer
+ VarVersionPaar cpaar = new VarVersionPaar(cexpr.id, -1);
+ if (!mapExprentMinTypes.containsKey(cpaar)) {
+ mapExprentMinTypes.put(cpaar, cexpr.getConsttype());
+ }
+ }
+ }
+
+ CheckTypesResult result = exprent.checkExprTypeBounds();
+
+ for (CheckTypesResult.ExprentTypePair entry : result.getLstMaxTypeExprents()) {
+ if (entry.type.type_family != CodeConstants.TYPE_FAMILY_OBJECT) {
+ changeExprentType(entry.exprent, entry.type, 1);
+ }
+ }
+
+ boolean res = true;
+ for (CheckTypesResult.ExprentTypePair entry : result.getLstMinTypeExprents()) {
+ res &= changeExprentType(entry.exprent, entry.type, 0);
+ }
+
+ return res;
+ }
+
+
+ private boolean changeExprentType(Exprent exprent, VarType newtype, int minmax) {
+
+ boolean res = true;
+
+ switch (exprent.type) {
+ case Exprent.EXPRENT_CONST:
+ ConstExprent cexpr = (ConstExprent)exprent;
+ VarType consttype = cexpr.getConsttype();
+
+ if (newtype.type_family > CodeConstants.TYPE_FAMILY_INTEGER || consttype.type_family > CodeConstants.TYPE_FAMILY_INTEGER) {
+ return true;
+ }
+ else if (newtype.type_family == CodeConstants.TYPE_FAMILY_INTEGER) {
+ VarType mininteger = new ConstExprent((Integer)((ConstExprent)exprent).getValue(), false).getConsttype();
+ if (mininteger.isStrictSuperset(newtype)) {
+ newtype = mininteger;
+ }
+ }
+ case Exprent.EXPRENT_VAR:
+ VarVersionPaar varpaar = null;
+ if (exprent.type == Exprent.EXPRENT_CONST) {
+ varpaar = new VarVersionPaar(((ConstExprent)exprent).id, -1);
+ }
+ else if (exprent.type == Exprent.EXPRENT_VAR) {
+ varpaar = new VarVersionPaar((VarExprent)exprent);
+ }
+
+ if (minmax == 0) { // min
+ VarType currentMinType = mapExprentMinTypes.get(varpaar);
+ VarType newMinType;
+ if (currentMinType == null || newtype.type_family > currentMinType.type_family) {
+ newMinType = newtype;
+ }
+ else if (newtype.type_family < currentMinType.type_family) {
+ return true;
+ }
+ else {
+ newMinType = VarType.getCommonSupertype(currentMinType, newtype);
+ }
+
+ mapExprentMinTypes.put(varpaar, newMinType);
+ if (exprent.type == Exprent.EXPRENT_CONST) {
+ ((ConstExprent)exprent).setConsttype(newMinType);
+ }
+
+ if (currentMinType != null && (newMinType.type_family > currentMinType.type_family ||
+ newMinType.isStrictSuperset(currentMinType))) {
+ return false;
+ }
+ }
+ else { // max
+ VarType currentMaxType = mapExprentMaxTypes.get(varpaar);
+ VarType newMaxType;
+ if (currentMaxType == null || newtype.type_family < currentMaxType.type_family) {
+ newMaxType = newtype;
+ }
+ else if (newtype.type_family > currentMaxType.type_family) {
+ return true;
+ }
+ else {
+ newMaxType = VarType.getCommonMinType(currentMaxType, newtype);
+ }
+
+ mapExprentMaxTypes.put(varpaar, newMaxType);
+ }
+ break;
+ case Exprent.EXPRENT_ASSIGNMENT:
+ return changeExprentType(((AssignmentExprent)exprent).getRight(), newtype, minmax);
+ case Exprent.EXPRENT_FUNCTION:
+ FunctionExprent func = (FunctionExprent)exprent;
+ switch (func.getFunctype()) {
+ case FunctionExprent.FUNCTION_IIF: // FIXME:
+ res &= changeExprentType(func.getLstOperands().get(1), newtype, minmax);
+ res &= changeExprentType(func.getLstOperands().get(2), newtype, minmax);
+ break;
+ case FunctionExprent.FUNCTION_AND:
+ case FunctionExprent.FUNCTION_OR:
+ case FunctionExprent.FUNCTION_XOR:
+ res &= changeExprentType(func.getLstOperands().get(0), newtype, minmax);
+ res &= changeExprentType(func.getLstOperands().get(1), newtype, minmax);
+ }
+ }
+
+ return res;
+ }
+
+ public HashMap<VarVersionPaar, VarType> getMapExprentMaxTypes() {
+ return mapExprentMaxTypes;
+ }
+
+ public HashMap<VarVersionPaar, VarType> getMapExprentMinTypes() {
+ return mapExprentMinTypes;
+ }
+
+ public HashMap<VarVersionPaar, Integer> getMapFinalVars() {
+ return mapFinalVars;
+ }
+
+ public void setVarType(VarVersionPaar varpaar, VarType type) {
+ mapExprentMinTypes.put(varpaar, type);
+ }
+
+ public VarType getVarType(VarVersionPaar varpaar) {
+ return mapExprentMinTypes.get(varpaar);
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionEdge.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionEdge.java
new file mode 100644
index 000000000000..9d9798279d41
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionEdge.java
@@ -0,0 +1,56 @@
+/*
+ * 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.modules.decompiler.vars;
+
+public class VarVersionEdge { // FIXME: can be removed?
+
+ public static final int EDGE_GENERAL = 0;
+ public static final int EDGE_PHANTOM = 1;
+
+ public int type;
+
+ public VarVersionNode source;
+
+ public VarVersionNode dest;
+
+ private int hashCode;
+
+ public VarVersionEdge(int type, VarVersionNode source, VarVersionNode dest) {
+ this.type = type;
+ this.source = source;
+ this.dest = dest;
+ this.hashCode = source.hashCode() ^ dest.hashCode() + type;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (o == null || !(o instanceof VarVersionEdge)) return false;
+
+ VarVersionEdge edge = (VarVersionEdge)o;
+ return type == edge.type && source == edge.source && dest == edge.dest;
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+
+ @Override
+ public String toString() {
+ return source.toString() + " ->" + type + "-> " + dest.toString();
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionNode.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionNode.java
new file mode 100644
index 000000000000..587c653dc3fb
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionNode.java
@@ -0,0 +1,80 @@
+/*
+ * 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.modules.decompiler.vars;
+
+import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraphNode;
+import org.jetbrains.java.decompiler.util.SFormsFastMapDirect;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class VarVersionNode implements IGraphNode {
+
+ public static final int FLAG_PHANTOM_FINEXIT = 2;
+
+ public int var;
+
+ public int version;
+
+ public Set<VarVersionEdge> succs = new HashSet<VarVersionEdge>();
+
+ public Set<VarVersionEdge> preds = new HashSet<VarVersionEdge>();
+
+ public int flags;
+
+ public SFormsFastMapDirect live = new SFormsFastMapDirect();
+
+
+ public VarVersionNode(int var, int version) {
+ this.var = var;
+ this.version = version;
+ }
+
+ public VarVersionPaar getVarPaar() {
+ return new VarVersionPaar(var, version);
+ }
+
+ public List<IGraphNode> getPredecessors() {
+ List<IGraphNode> lst = new ArrayList<IGraphNode>(preds.size());
+ for (VarVersionEdge edge : preds) {
+ lst.add(edge.source);
+ }
+ return lst;
+ }
+
+ public void removeSuccessor(VarVersionEdge edge) {
+ succs.remove(edge);
+ }
+
+ public void removePredecessor(VarVersionEdge edge) {
+ preds.remove(edge);
+ }
+
+ public void addSuccessor(VarVersionEdge edge) {
+ succs.add(edge);
+ }
+
+ public void addPredecessor(VarVersionEdge edge) {
+ preds.add(edge);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + var + "_" + version + ")";
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionPaar.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionPaar.java
new file mode 100644
index 000000000000..5f3e520a84d0
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionPaar.java
@@ -0,0 +1,63 @@
+/*
+ * 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.modules.decompiler.vars;
+
+import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;
+
+public class VarVersionPaar {
+
+ public int var;
+ public int version;
+
+ private int hashCode = -1;
+
+ public VarVersionPaar(int var, int version) {
+ this.var = var;
+ this.version = version;
+ }
+
+ public VarVersionPaar(Integer var, Integer version) {
+ this.var = var.intValue();
+ this.version = version.intValue();
+ }
+
+ public VarVersionPaar(VarExprent var) {
+ this.var = var.getIndex();
+ this.version = var.getVersion();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (o == null || !(o instanceof VarVersionPaar)) return false;
+
+ VarVersionPaar paar = (VarVersionPaar)o;
+ return var == paar.var && version == paar.version;
+ }
+
+ @Override
+ public int hashCode() {
+ if (hashCode == -1) {
+ hashCode = this.var * 3 + this.version;
+ }
+ return hashCode;
+ }
+
+ @Override
+ public String toString() {
+ return "(" + var + "," + version + ")";
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsGraph.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsGraph.java
new file mode 100644
index 000000000000..f839a05819df
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsGraph.java
@@ -0,0 +1,167 @@
+/*
+ * 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.modules.decompiler.vars;
+
+import org.jetbrains.java.decompiler.modules.decompiler.decompose.GenericDominatorEngine;
+import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraph;
+import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraphNode;
+import org.jetbrains.java.decompiler.util.VBStyleCollection;
+
+import java.util.*;
+
+
+public class VarVersionsGraph {
+
+ public int counter = 0;
+
+ public VBStyleCollection<VarVersionNode, VarVersionPaar> nodes = new VBStyleCollection<VarVersionNode, VarVersionPaar>();
+
+ private GenericDominatorEngine engine;
+
+ public VarVersionNode createNode(VarVersionPaar ver) {
+ VarVersionNode node;
+ nodes.addWithKey(node = new VarVersionNode(ver.var, ver.version), ver);
+ return node;
+ }
+
+ public void addNodes(Collection<VarVersionNode> colnodes, Collection<VarVersionPaar> colpaars) {
+ nodes.addAllWithKey(colnodes, colpaars);
+ }
+
+ public boolean isDominatorSet(VarVersionNode node, HashSet<VarVersionNode> domnodes) {
+
+ if (domnodes.size() == 1) {
+ return engine.isDominator(node, domnodes.iterator().next());
+ }
+ else {
+
+ HashSet<VarVersionNode> marked = new HashSet<VarVersionNode>();
+
+ if (domnodes.contains(node)) {
+ return true;
+ }
+
+ LinkedList<VarVersionNode> lstNodes = new LinkedList<VarVersionNode>();
+ lstNodes.add(node);
+
+ while (!lstNodes.isEmpty()) {
+
+ VarVersionNode nd = lstNodes.remove(0);
+ if (marked.contains(nd)) {
+ continue;
+ }
+ else {
+ marked.add(nd);
+ }
+
+ if (nd.preds.isEmpty()) {
+ return false;
+ }
+
+ for (VarVersionEdge edge : nd.preds) {
+ VarVersionNode pred = edge.source;
+ if (!marked.contains(pred) && !domnodes.contains(pred)) {
+ lstNodes.add(pred);
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public void initDominators() {
+
+ final HashSet<VarVersionNode> roots = new HashSet<VarVersionNode>();
+
+ for (VarVersionNode node : nodes) {
+ if (node.preds.isEmpty()) {
+ roots.add(node);
+ }
+ }
+
+ engine = new GenericDominatorEngine(new IGraph() {
+ public List<? extends IGraphNode> getReversePostOrderList() {
+ return getReversedPostOrder(roots);
+ }
+
+ public Set<? extends IGraphNode> getRoots() {
+ return new HashSet<IGraphNode>(roots);
+ }
+ });
+
+ engine.initialize();
+ }
+
+ private static LinkedList<VarVersionNode> getReversedPostOrder(Collection<VarVersionNode> roots) {
+
+ LinkedList<VarVersionNode> lst = new LinkedList<VarVersionNode>();
+ HashSet<VarVersionNode> setVisited = new HashSet<VarVersionNode>();
+
+ for (VarVersionNode root : roots) {
+
+ LinkedList<VarVersionNode> lstTemp = new LinkedList<VarVersionNode>();
+ addToReversePostOrderListIterative(root, lstTemp, setVisited);
+
+ lst.addAll(lstTemp);
+ }
+
+ return lst;
+ }
+
+ private static void addToReversePostOrderListIterative(VarVersionNode root, List<VarVersionNode> lst, HashSet<VarVersionNode> setVisited) {
+
+ HashMap<VarVersionNode, List<VarVersionEdge>> mapNodeSuccs = new HashMap<VarVersionNode, List<VarVersionEdge>>();
+
+ LinkedList<VarVersionNode> stackNode = new LinkedList<VarVersionNode>();
+ LinkedList<Integer> stackIndex = new LinkedList<Integer>();
+
+ stackNode.add(root);
+ stackIndex.add(0);
+
+ while (!stackNode.isEmpty()) {
+
+ VarVersionNode node = stackNode.getLast();
+ int index = stackIndex.removeLast();
+
+ setVisited.add(node);
+
+ List<VarVersionEdge> lstSuccs = mapNodeSuccs.get(node);
+ if (lstSuccs == null) {
+ mapNodeSuccs.put(node, lstSuccs = new ArrayList<VarVersionEdge>(node.succs));
+ }
+
+ for (; index < lstSuccs.size(); index++) {
+ VarVersionNode succ = lstSuccs.get(index).dest;
+
+ if (!setVisited.contains(succ)) {
+ stackIndex.add(index + 1);
+
+ stackNode.add(succ);
+ stackIndex.add(0);
+
+ break;
+ }
+ }
+
+ if (index == lstSuccs.size()) {
+ lst.add(0, node);
+
+ stackNode.removeLast();
+ }
+ }
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java
new file mode 100644
index 000000000000..06b7216c86fa
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java
@@ -0,0 +1,334 @@
+/*
+ * 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.modules.decompiler.vars;
+
+import org.jetbrains.java.decompiler.code.CodeConstants;
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;
+import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph;
+import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper;
+import org.jetbrains.java.decompiler.modules.decompiler.sforms.SSAConstructorSparseEx;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
+import org.jetbrains.java.decompiler.struct.StructMethod;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+public class VarVersionsProcessor {
+
+ private HashMap<Integer, Integer> mapOriginalVarIndices = new HashMap<Integer, Integer>();
+
+ private VarTypeProcessor typeproc;
+
+ public void setVarVersions(RootStatement root) {
+
+ StructMethod mt = (StructMethod)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD);
+
+ SSAConstructorSparseEx ssa = new SSAConstructorSparseEx();
+ ssa.splitVariables(root, mt);
+
+ FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();
+ DirectGraph dgraph = flatthelper.buildDirectGraph(root);
+
+ // System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
+
+ mergePhiVersions(ssa, dgraph);
+
+ // System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
+
+ typeproc = new VarTypeProcessor();
+ typeproc.calculateVarTypes(root, dgraph);
+
+ simpleMerge(typeproc, dgraph, mt);
+
+ // FIXME: advanced merging
+
+ eliminateNonJavaTypes(typeproc);
+
+ setNewVarIndices(typeproc, dgraph);
+ }
+
+ private static void mergePhiVersions(SSAConstructorSparseEx ssa, DirectGraph dgraph) {
+
+ // collect phi versions
+ List<HashSet<VarVersionPaar>> lst = new ArrayList<HashSet<VarVersionPaar>>();
+ for (Entry<VarVersionPaar, FastSparseSet<Integer>> ent : ssa.getPhi().entrySet()) {
+ HashSet<VarVersionPaar> set = new HashSet<VarVersionPaar>();
+ set.add(ent.getKey());
+ for (Integer vers : ent.getValue()) {
+ set.add(new VarVersionPaar(ent.getKey().var, vers.intValue()));
+ }
+
+ for (int i = lst.size() - 1; i >= 0; i--) {
+ HashSet<VarVersionPaar> tset = lst.get(i);
+ HashSet<VarVersionPaar> intersection = new HashSet<VarVersionPaar>(set);
+ intersection.retainAll(tset);
+
+ if (!intersection.isEmpty()) {
+ set.addAll(tset);
+ lst.remove(i);
+ }
+ }
+
+ lst.add(set);
+ }
+
+ final HashMap<VarVersionPaar, Integer> phivers = new HashMap<VarVersionPaar, Integer>();
+ for (HashSet<VarVersionPaar> set : lst) {
+ int min = Integer.MAX_VALUE;
+ for (VarVersionPaar paar : set) {
+ if (paar.version < min) {
+ min = paar.version;
+ }
+ }
+
+ for (VarVersionPaar paar : set) {
+ phivers.put(new VarVersionPaar(paar.var, paar.version), min);
+ }
+ }
+
+
+ dgraph.iterateExprents(new DirectGraph.ExprentIterator() {
+ public int processExprent(Exprent exprent) {
+ List<Exprent> lst = exprent.getAllExprents(true);
+ lst.add(exprent);
+
+ for (Exprent expr : lst) {
+ if (expr.type == Exprent.EXPRENT_VAR) {
+ VarExprent var = (VarExprent)expr;
+ Integer vers = phivers.get(new VarVersionPaar(var));
+ if (vers != null) {
+ var.setVersion(vers);
+ }
+ }
+ }
+ return 0;
+ }
+ });
+ }
+
+ private static void eliminateNonJavaTypes(VarTypeProcessor typeproc) {
+
+ HashMap<VarVersionPaar, VarType> mapExprentMaxTypes = typeproc.getMapExprentMaxTypes();
+ HashMap<VarVersionPaar, VarType> mapExprentMinTypes = typeproc.getMapExprentMinTypes();
+
+ HashSet<VarVersionPaar> set = new HashSet<VarVersionPaar>(mapExprentMinTypes.keySet());
+ for (VarVersionPaar paar : set) {
+ VarType type = mapExprentMinTypes.get(paar);
+ VarType maxtype = mapExprentMaxTypes.get(paar);
+
+ if (type.type == CodeConstants.TYPE_BYTECHAR || type.type == CodeConstants.TYPE_SHORTCHAR) {
+ if (maxtype != null && maxtype.type == CodeConstants.TYPE_CHAR) {
+ type = VarType.VARTYPE_CHAR;
+ }
+ else {
+ type = type.type == CodeConstants.TYPE_BYTECHAR ? VarType.VARTYPE_BYTE : VarType.VARTYPE_SHORT;
+ }
+ mapExprentMinTypes.put(paar, type);
+ //} else if(type.type == CodeConstants.TYPE_CHAR && (maxtype == null || maxtype.type == CodeConstants.TYPE_INT)) { // when possible, lift char to int
+ // mapExprentMinTypes.put(paar, VarType.VARTYPE_INT);
+ }
+ else if (type.type == CodeConstants.TYPE_NULL) {
+ mapExprentMinTypes.put(paar, VarType.VARTYPE_OBJECT);
+ }
+ }
+ }
+
+ private static void simpleMerge(VarTypeProcessor typeproc, DirectGraph dgraph, StructMethod mt) {
+
+ HashMap<VarVersionPaar, VarType> mapExprentMaxTypes = typeproc.getMapExprentMaxTypes();
+ HashMap<VarVersionPaar, VarType> mapExprentMinTypes = typeproc.getMapExprentMinTypes();
+
+ HashMap<Integer, HashSet<Integer>> mapVarVersions = new HashMap<Integer, HashSet<Integer>>();
+
+ for (VarVersionPaar varpaar : mapExprentMinTypes.keySet()) {
+ if (varpaar.version >= 0) { // don't merge constants
+ HashSet<Integer> set = mapVarVersions.get(varpaar.var);
+ if (set == null) {
+ set = new HashSet<Integer>();
+ mapVarVersions.put(varpaar.var, set);
+ }
+ set.add(varpaar.version);
+ }
+ }
+
+ boolean is_method_static = mt.hasModifier(CodeConstants.ACC_STATIC);
+
+ final HashMap<VarVersionPaar, Integer> mapMergedVersions = new HashMap<VarVersionPaar, Integer>();
+
+ for (Entry<Integer, HashSet<Integer>> ent : mapVarVersions.entrySet()) {
+
+ if (ent.getValue().size() > 1) {
+ List<Integer> lstVersions = new ArrayList<Integer>(ent.getValue());
+ Collections.sort(lstVersions);
+
+ for (int i = 0; i < lstVersions.size(); i++) {
+ VarVersionPaar firstpaar = new VarVersionPaar(ent.getKey(), lstVersions.get(i));
+ VarType firsttype = mapExprentMinTypes.get(firstpaar);
+
+ if (firstpaar.var == 0 && firstpaar.version == 1 && !is_method_static) {
+ continue; // don't merge 'this' variable
+ }
+
+ for (int j = i + 1; j < lstVersions.size(); j++) {
+ VarVersionPaar secpaar = new VarVersionPaar(ent.getKey(), lstVersions.get(j));
+ VarType sectype = mapExprentMinTypes.get(secpaar);
+
+ if (firsttype.equals(sectype) || (firsttype.equals(VarType.VARTYPE_NULL) && sectype.type == CodeConstants.TYPE_OBJECT)
+ || (sectype.equals(VarType.VARTYPE_NULL) && firsttype.type == CodeConstants.TYPE_OBJECT)) {
+
+ VarType firstMaxType = mapExprentMaxTypes.get(firstpaar);
+ VarType secMaxType = mapExprentMaxTypes.get(secpaar);
+ mapExprentMaxTypes.put(firstpaar, firstMaxType == null ? secMaxType :
+ (secMaxType == null ? firstMaxType : VarType.getCommonMinType(firstMaxType, secMaxType)));
+
+
+ mapMergedVersions.put(secpaar, firstpaar.version);
+ mapExprentMaxTypes.remove(secpaar);
+ mapExprentMinTypes.remove(secpaar);
+
+ if (firsttype.equals(VarType.VARTYPE_NULL)) {
+ mapExprentMinTypes.put(firstpaar, sectype);
+ firsttype = sectype;
+ }
+
+ typeproc.getMapFinalVars().put(firstpaar, VarTypeProcessor.VAR_NONFINAL);
+
+ lstVersions.remove(j);
+ j--;
+ }
+ }
+ }
+ }
+ }
+
+ if (!mapMergedVersions.isEmpty()) {
+ dgraph.iterateExprents(new DirectGraph.ExprentIterator() {
+ public int processExprent(Exprent exprent) {
+ List<Exprent> lst = exprent.getAllExprents(true);
+ lst.add(exprent);
+
+ for (Exprent expr : lst) {
+ if (expr.type == Exprent.EXPRENT_VAR) {
+ VarExprent varex = (VarExprent)expr;
+ Integer newversion = mapMergedVersions.get(new VarVersionPaar(varex));
+ if (newversion != null) {
+ varex.setVersion(newversion);
+ }
+ }
+ }
+
+ return 0;
+ }
+ });
+ }
+ }
+
+ private void setNewVarIndices(VarTypeProcessor typeproc, DirectGraph dgraph) {
+
+ final HashMap<VarVersionPaar, VarType> mapExprentMaxTypes = typeproc.getMapExprentMaxTypes();
+ HashMap<VarVersionPaar, VarType> mapExprentMinTypes = typeproc.getMapExprentMinTypes();
+ HashMap<VarVersionPaar, Integer> mapFinalVars = typeproc.getMapFinalVars();
+
+ CounterContainer ccon = DecompilerContext.getCounterContainer();
+
+ final HashMap<VarVersionPaar, Integer> mapVarPaar = new HashMap<VarVersionPaar, Integer>();
+ HashMap<Integer, Integer> mapOriginalVarIndices = new HashMap<Integer, Integer>();
+
+ // map var-version paars on new var indexes
+ HashSet<VarVersionPaar> set = new HashSet<VarVersionPaar>(mapExprentMinTypes.keySet());
+ for (VarVersionPaar vpaar : set) {
+
+ if (vpaar.version >= 0) {
+ int newindex = vpaar.version == 1 ? vpaar.var :
+ ccon.getCounterAndIncrement(CounterContainer.VAR_COUNTER);
+
+ VarVersionPaar newvar = new VarVersionPaar(newindex, 0);
+
+ mapExprentMinTypes.put(newvar, mapExprentMinTypes.get(vpaar));
+ mapExprentMaxTypes.put(newvar, mapExprentMaxTypes.get(vpaar));
+
+ if (mapFinalVars.containsKey(vpaar)) {
+ mapFinalVars.put(newvar, mapFinalVars.remove(vpaar));
+ }
+
+ mapVarPaar.put(vpaar, newindex);
+ mapOriginalVarIndices.put(newindex, vpaar.var);
+ }
+ }
+
+ // set new vars
+ dgraph.iterateExprents(new DirectGraph.ExprentIterator() {
+ public int processExprent(Exprent exprent) {
+ List<Exprent> lst = exprent.getAllExprents(true);
+ lst.add(exprent);
+
+ for (Exprent expr : lst) {
+ if (expr.type == Exprent.EXPRENT_VAR) {
+ VarExprent varex = (VarExprent)expr;
+ Integer newvarindex = mapVarPaar.get(new VarVersionPaar(varex));
+ if (newvarindex != null) {
+ varex.setIndex(newvarindex);
+ varex.setVersion(0);
+ }
+ }
+ else if (expr.type == Exprent.EXPRENT_CONST) {
+ VarType maxType = mapExprentMaxTypes.get(new VarVersionPaar(expr.id, -1));
+ if (maxType != null && maxType.equals(VarType.VARTYPE_CHAR)) {
+ ((ConstExprent)expr).setConsttype(maxType);
+ }
+ }
+ }
+
+ return 0;
+ }
+ });
+
+ this.mapOriginalVarIndices = mapOriginalVarIndices;
+ }
+
+ public VarType getVarType(VarVersionPaar varpaar) {
+ return typeproc == null ? null : typeproc.getVarType(varpaar);
+ }
+
+ public void setVarType(VarVersionPaar varpaar, VarType type) {
+ typeproc.setVarType(varpaar, type);
+ }
+
+ public int getVarFinal(VarVersionPaar varpaar) {
+
+ int ret = VarTypeProcessor.VAR_FINAL;
+ if (typeproc != null) {
+ Integer fin = typeproc.getMapFinalVars().get(varpaar);
+ ret = fin == null ? VarTypeProcessor.VAR_FINAL : fin.intValue();
+ }
+
+ return ret;
+ }
+
+ public void setVarFinal(VarVersionPaar varpaar, int finaltype) {
+ typeproc.getMapFinalVars().put(varpaar, finaltype);
+ }
+
+ public HashMap<Integer, Integer> getMapOriginalVarIndices() {
+ return mapOriginalVarIndices;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/renamer/ClassWrapperNode.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/renamer/ClassWrapperNode.java
new file mode 100644
index 000000000000..273579a56373
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/renamer/ClassWrapperNode.java
@@ -0,0 +1,55 @@
+/*
+ * 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.modules.renamer;
+
+import org.jetbrains.java.decompiler.struct.StructClass;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ClassWrapperNode {
+
+ private StructClass classStruct;
+
+ private ClassWrapperNode superclass;
+
+ private List<ClassWrapperNode> subclasses = new ArrayList<ClassWrapperNode>();
+
+ public ClassWrapperNode(StructClass cl) {
+ this.classStruct = cl;
+ }
+
+ public void addSubclass(ClassWrapperNode node) {
+ node.setSuperclass(this);
+ subclasses.add(node);
+ }
+
+ public StructClass getClassStruct() {
+ return classStruct;
+ }
+
+ public List<ClassWrapperNode> getSubclasses() {
+ return subclasses;
+ }
+
+ public ClassWrapperNode getSuperclass() {
+ return superclass;
+ }
+
+ public void setSuperclass(ClassWrapperNode superclass) {
+ this.superclass = superclass;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/renamer/ConverterHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/renamer/ConverterHelper.java
new file mode 100644
index 000000000000..f2cb1ec0e657
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/renamer/ConverterHelper.java
@@ -0,0 +1,143 @@
+/*
+ * 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.modules.renamer;
+
+import org.jetbrains.java.decompiler.main.extern.IIdentifierRenamer;
+
+import java.util.HashSet;
+
+public class ConverterHelper implements IIdentifierRenamer {
+
+ private static HashSet<String> setReserved = new HashSet<String>();
+
+ static {
+ setReserved.add("abstract");
+ setReserved.add("do");
+ setReserved.add("if");
+ setReserved.add("package");
+ setReserved.add("synchronized");
+ setReserved.add("boolean");
+ setReserved.add("double");
+ setReserved.add("implements");
+ setReserved.add("private");
+ setReserved.add("this");
+ setReserved.add("break");
+ setReserved.add("else");
+ setReserved.add("import");
+ setReserved.add("protected");
+ setReserved.add("throw");
+ setReserved.add("byte");
+ setReserved.add("extends");
+ setReserved.add("instanceof");
+ setReserved.add("public");
+ setReserved.add("throws");
+ setReserved.add("case");
+ setReserved.add("false");
+ setReserved.add("int");
+ setReserved.add("return");
+ setReserved.add("transient");
+ setReserved.add("catch");
+ setReserved.add("final");
+ setReserved.add("interface");
+ setReserved.add("short");
+ setReserved.add("true");
+ setReserved.add("char");
+ setReserved.add("finally");
+ setReserved.add("long");
+ setReserved.add("static");
+ setReserved.add("try");
+ setReserved.add("class");
+ setReserved.add("float");
+ setReserved.add("native");
+ setReserved.add("strictfp");
+ setReserved.add("void");
+ setReserved.add("const");
+ setReserved.add("for");
+ setReserved.add("new");
+ setReserved.add("super");
+ setReserved.add("volatile");
+ setReserved.add("continue");
+ setReserved.add("goto");
+ setReserved.add("null");
+ setReserved.add("switch");
+ setReserved.add("while");
+ setReserved.add("default");
+ setReserved.add("assert");
+ setReserved.add("enum");
+ }
+
+ private int class_counter = 0;
+
+ private int field_counter = 0;
+
+ private int method_counter = 0;
+
+ private HashSet<String> setNonStandardClassNames = new HashSet<String>();
+
+ public boolean toBeRenamed(int element_type, String classname, String element, String descriptor) {
+ String value = (element_type == IIdentifierRenamer.ELEMENT_CLASS) ? classname : element;
+ return value == null || value.length() == 0 || value.length() <= 2 || setReserved.contains(value) || Character.isDigit(value.charAt(0));
+ }
+
+ // TODO: consider possible conflicts with not renamed classes, fields and methods!
+ // We should get all relevant information here.
+ public String getNextClassname(String fullname, String shortname) {
+
+ if (shortname == null) {
+ return "class_" + (class_counter++);
+ }
+
+ int index = 0;
+ while (Character.isDigit(shortname.charAt(index))) {
+ index++;
+ }
+
+ if (index == 0 || index == shortname.length()) {
+ return "class_" + (class_counter++);
+ }
+ else {
+ String name = shortname.substring(index);
+
+ if (setNonStandardClassNames.contains(name)) {
+ return "Inner" + name + "_" + (class_counter++);
+ }
+ else {
+ setNonStandardClassNames.add(name);
+ return "Inner" + name;
+ }
+ }
+ }
+
+ public String getNextFieldname(String classname, String field, String descriptor) {
+ return "field_" + (field_counter++);
+ }
+
+ public String getNextMethodname(String classname, String method, String descriptor) {
+ return "method_" + (method_counter++);
+ }
+
+ // *****************************************************************************
+ // static methods
+ // *****************************************************************************
+
+ public static String getSimpleClassName(String fullname) {
+ return fullname.substring(fullname.lastIndexOf('/') + 1);
+ }
+
+ public static String replaceSimpleClassName(String fullname, String newname) {
+ return fullname.substring(0, fullname.lastIndexOf('/') + 1) + newname;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java
new file mode 100644
index 000000000000..9ed3578d3d55
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java
@@ -0,0 +1,463 @@
+/*
+ * 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.modules.renamer;
+
+import org.jetbrains.java.decompiler.code.CodeConstants;
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
+import org.jetbrains.java.decompiler.main.extern.IIdentifierRenamer;
+import org.jetbrains.java.decompiler.struct.StructClass;
+import org.jetbrains.java.decompiler.struct.StructContext;
+import org.jetbrains.java.decompiler.struct.StructField;
+import org.jetbrains.java.decompiler.struct.StructMethod;
+import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor;
+import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+import org.jetbrains.java.decompiler.util.VBStyleCollection;
+
+import java.io.IOException;
+import java.util.*;
+
+public class IdentifierConverter {
+
+ private StructContext context;
+
+ private IIdentifierRenamer helper;
+
+ private PoolInterceptor interceptor;
+
+ private List<ClassWrapperNode> rootClasses = new ArrayList<ClassWrapperNode>();
+
+ private List<ClassWrapperNode> rootInterfaces = new ArrayList<ClassWrapperNode>();
+
+ private HashMap<String, HashMap<String, String>> interfaceNameMaps = new HashMap<String, HashMap<String, String>>();
+
+ public void rename(StructContext context) {
+
+ try {
+ this.context = context;
+
+ String user_class = (String)DecompilerContext.getProperty(IFernflowerPreferences.USER_RENAMER_CLASS);
+ if (user_class != null) {
+ try {
+ helper = (IIdentifierRenamer)IdentifierConverter.class.getClassLoader().loadClass(user_class).newInstance();
+ }
+ catch (Exception ex) {
+ // ignore errors
+ }
+ }
+
+ if (helper == null) {
+ helper = new ConverterHelper();
+ }
+
+ interceptor = new PoolInterceptor(helper);
+
+ buildInheritanceTree();
+
+ renameAllClasses();
+
+ renameInterfaces();
+
+ renameClasses();
+
+ DecompilerContext.setPoolInterceptor(interceptor);
+ context.reloadContext();
+ }
+ catch (IOException ex) {
+ throw new RuntimeException("Renaming failed!");
+ }
+ }
+
+ private void renameClasses() {
+
+ List<ClassWrapperNode> lstClasses = getReversePostOrderListIterative(rootClasses);
+
+ HashMap<String, HashMap<String, String>> classNameMaps = new HashMap<String, HashMap<String, String>>();
+
+ for (ClassWrapperNode node : lstClasses) {
+
+ StructClass cl = node.getClassStruct();
+ HashMap<String, String> names = new HashMap<String, String>();
+
+ // merge informations on super class
+ if (cl.superClass != null) {
+ HashMap<String, String> mapClass = classNameMaps.get(cl.superClass.getString());
+ if (mapClass != null) {
+ names.putAll(mapClass);
+ }
+ }
+
+ // merge informations on interfaces
+ for (String intrName : cl.getInterfaceNames()) {
+ HashMap<String, String> mapInt = interfaceNameMaps.get(intrName);
+ if (mapInt != null) {
+ names.putAll(mapInt);
+ }
+ else {
+ StructClass clintr = context.getClass(intrName);
+ if (clintr != null) {
+ names.putAll(processExternalInterface(clintr));
+ }
+ }
+ }
+
+ renameClassIdentifiers(cl, names);
+
+ if (!node.getSubclasses().isEmpty()) {
+ classNameMaps.put(cl.qualifiedName, names);
+ }
+ }
+ }
+
+ private HashMap<String, String> processExternalInterface(StructClass cl) {
+
+ HashMap<String, String> names = new HashMap<String, String>();
+
+ for (String intrName : cl.getInterfaceNames()) {
+
+ HashMap<String, String> mapInt = interfaceNameMaps.get(intrName);
+ if (mapInt != null) {
+ names.putAll(mapInt);
+ }
+ else {
+ StructClass clintr = context.getClass(intrName);
+ if (clintr != null) {
+ names.putAll(processExternalInterface(clintr));
+ }
+ }
+ }
+
+ renameClassIdentifiers(cl, names);
+
+ return names;
+ }
+
+ private void renameInterfaces() {
+
+ List<ClassWrapperNode> lstInterfaces = getReversePostOrderListIterative(rootInterfaces);
+
+ HashMap<String, HashMap<String, String>> interfaceNameMaps = new HashMap<String, HashMap<String, String>>();
+
+ // rename methods and fields
+ for (ClassWrapperNode node : lstInterfaces) {
+
+ StructClass cl = node.getClassStruct();
+ HashMap<String, String> names = new HashMap<String, String>();
+
+ // merge informations on super interfaces
+ for (String intrName : cl.getInterfaceNames()) {
+ HashMap<String, String> mapInt = interfaceNameMaps.get(intrName);
+ if (mapInt != null) {
+ names.putAll(mapInt);
+ }
+ }
+
+ renameClassIdentifiers(cl, names);
+
+ interfaceNameMaps.put(cl.qualifiedName, names);
+ }
+
+ this.interfaceNameMaps = interfaceNameMaps;
+ }
+
+ private void renameAllClasses() {
+
+ // order not important
+ List<ClassWrapperNode> lstAllClasses = new ArrayList<ClassWrapperNode>(getReversePostOrderListIterative(rootInterfaces));
+ lstAllClasses.addAll(getReversePostOrderListIterative(rootClasses));
+
+ // rename all interfaces and classes
+ for (ClassWrapperNode node : lstAllClasses) {
+ renameClass(node.getClassStruct());
+ }
+ }
+
+ private void renameClass(StructClass cl) {
+
+ if (!cl.isOwn()) {
+ return;
+ }
+
+ String classOldFullName = cl.qualifiedName;
+
+ // TODO: rename packages
+ String clsimplename = ConverterHelper.getSimpleClassName(classOldFullName);
+ if (helper.toBeRenamed(IIdentifierRenamer.ELEMENT_CLASS, clsimplename, null, null)) {
+ String classNewFullName;
+
+ do {
+ classNewFullName = ConverterHelper.replaceSimpleClassName(classOldFullName,
+ helper.getNextClassname(classOldFullName, ConverterHelper
+ .getSimpleClassName(classOldFullName)));
+ }
+ while (context.getClasses().containsKey(classNewFullName));
+
+ interceptor.addName(classOldFullName, classNewFullName);
+ }
+ }
+
+ private void renameClassIdentifiers(StructClass cl, HashMap<String, String> names) {
+
+ // all classes are already renamed
+ String classOldFullName = cl.qualifiedName;
+ String classNewFullName = interceptor.getName(classOldFullName);
+
+ if (classNewFullName == null) {
+ classNewFullName = classOldFullName;
+ }
+
+ // methods
+ HashSet<String> setMethodNames = new HashSet<String>();
+ for (StructMethod md : cl.getMethods()) {
+ setMethodNames.add(md.getName());
+ }
+
+ VBStyleCollection<StructMethod, String> methods = cl.getMethods();
+ for (int i = 0; i < methods.size(); i++) {
+
+ StructMethod mt = methods.get(i);
+ String key = methods.getKey(i);
+
+ boolean isPrivate = mt.hasModifier(CodeConstants.ACC_PRIVATE);
+
+ String name = mt.getName();
+ if (!cl.isOwn() || mt.hasModifier(CodeConstants.ACC_NATIVE)) {
+ // external and native methods must not be renamed
+ if (!isPrivate) {
+ names.put(key, name);
+ }
+ }
+ else if (helper.toBeRenamed(IIdentifierRenamer.ELEMENT_METHOD, classOldFullName, name, mt.getDescriptor())) {
+ if (isPrivate || !names.containsKey(key)) {
+ do {
+ name = helper.getNextMethodname(classOldFullName, name, mt.getDescriptor());
+ }
+ while (setMethodNames.contains(name));
+
+ if (!isPrivate) {
+ names.put(key, name);
+ }
+ }
+ else {
+ name = names.get(key);
+ }
+
+ interceptor.addName(classOldFullName + " " + mt.getName() + " " + mt.getDescriptor(),
+ classNewFullName + " " + name + " " + buildNewDescriptor(false, mt.getDescriptor()));
+ }
+ }
+
+ // external fields are not being renamed
+ if (!cl.isOwn()) {
+ return;
+ }
+
+ // fields
+ // FIXME: should overloaded fields become the same name?
+ HashSet<String> setFieldNames = new HashSet<String>();
+ for (StructField fd : cl.getFields()) {
+ setFieldNames.add(fd.getName());
+ }
+
+ for (StructField fd : cl.getFields()) {
+ if (helper.toBeRenamed(IIdentifierRenamer.ELEMENT_FIELD, classOldFullName, fd.getName(), fd.getDescriptor())) {
+ String newname;
+
+ do {
+ newname = helper.getNextFieldname(classOldFullName, fd.getName(), fd.getDescriptor());
+ }
+ while (setFieldNames.contains(newname));
+
+ interceptor.addName(classOldFullName + " " + fd.getName() + " " + fd.getDescriptor(),
+ classNewFullName + " " + newname + " " + buildNewDescriptor(true, fd.getDescriptor()));
+ }
+ }
+ }
+
+ private String buildNewDescriptor(boolean isField, String descriptor) {
+
+ boolean updated = false;
+
+ if (isField) {
+ FieldDescriptor fd = FieldDescriptor.parseDescriptor(descriptor);
+
+ VarType ftype = fd.type;
+ if (ftype.type == CodeConstants.TYPE_OBJECT) {
+ String newclname = interceptor.getName(ftype.value);
+ if (newclname != null) {
+ ftype.value = newclname;
+ updated = true;
+ }
+ }
+
+ if (updated) {
+ return fd.getDescriptor();
+ }
+ }
+ else {
+
+ MethodDescriptor md = MethodDescriptor.parseDescriptor(descriptor);
+ // params
+ for (VarType partype : md.params) {
+ if (partype.type == CodeConstants.TYPE_OBJECT) {
+ String newclname = interceptor.getName(partype.value);
+ if (newclname != null) {
+ partype.value = newclname;
+ updated = true;
+ }
+ }
+ }
+
+ // return value
+ if (md.ret.type == CodeConstants.TYPE_OBJECT) {
+ String newclname = interceptor.getName(md.ret.value);
+ if (newclname != null) {
+ md.ret.value = newclname;
+ updated = true;
+ }
+ }
+
+ if (updated) {
+ return md.getDescriptor();
+ }
+ }
+
+ return descriptor;
+ }
+
+ private static List<ClassWrapperNode> getReversePostOrderListIterative(List<ClassWrapperNode> roots) {
+
+ List<ClassWrapperNode> res = new ArrayList<ClassWrapperNode>();
+
+ LinkedList<ClassWrapperNode> stackNode = new LinkedList<ClassWrapperNode>();
+ LinkedList<Integer> stackIndex = new LinkedList<Integer>();
+
+ HashSet<ClassWrapperNode> setVisited = new HashSet<ClassWrapperNode>();
+
+ for (ClassWrapperNode root : roots) {
+ stackNode.add(root);
+ stackIndex.add(0);
+ }
+
+ while (!stackNode.isEmpty()) {
+
+ ClassWrapperNode node = stackNode.getLast();
+ int index = stackIndex.removeLast();
+
+ setVisited.add(node);
+
+ List<ClassWrapperNode> lstSubs = node.getSubclasses();
+
+ for (; index < lstSubs.size(); index++) {
+ ClassWrapperNode sub = lstSubs.get(index);
+ if (!setVisited.contains(sub)) {
+ stackIndex.add(index + 1);
+
+ stackNode.add(sub);
+ stackIndex.add(0);
+
+ break;
+ }
+ }
+
+ if (index == lstSubs.size()) {
+ res.add(0, node);
+
+ stackNode.removeLast();
+ }
+ }
+
+ return res;
+ }
+
+
+ private void buildInheritanceTree() {
+
+ Map<String, ClassWrapperNode> nodes = new HashMap<String, ClassWrapperNode>();
+ Map<String, StructClass> classes = context.getClasses();
+
+ List<ClassWrapperNode> rootClasses = new ArrayList<ClassWrapperNode>();
+ List<ClassWrapperNode> rootInterfaces = new ArrayList<ClassWrapperNode>();
+
+ for (StructClass cl : classes.values()) {
+
+ if (!cl.isOwn()) {
+ continue;
+ }
+
+ LinkedList<StructClass> stack = new LinkedList<StructClass>();
+ LinkedList<ClassWrapperNode> stackSubnodes = new LinkedList<ClassWrapperNode>();
+
+ stack.add(cl);
+ stackSubnodes.add(null);
+
+ while (!stack.isEmpty()) {
+
+ StructClass clstr = stack.removeFirst();
+ ClassWrapperNode child = stackSubnodes.removeFirst();
+
+ ClassWrapperNode node = nodes.get(clstr.qualifiedName);
+ boolean isNewNode = (node == null);
+
+ if (isNewNode) {
+ nodes.put(clstr.qualifiedName, node = new ClassWrapperNode(clstr));
+ }
+
+ if (child != null) {
+ node.addSubclass(child);
+ }
+
+ if (!isNewNode) {
+ break;
+ }
+ else {
+ boolean isInterface = clstr.hasModifier(CodeConstants.ACC_INTERFACE);
+ boolean found_parent = false;
+
+ if (isInterface) {
+ for (String intrName : clstr.getInterfaceNames()) {
+ StructClass clparent = classes.get(intrName);
+ if (clparent != null) {
+ stack.add(clparent);
+ stackSubnodes.add(node);
+ found_parent = true;
+ }
+ }
+ }
+ else {
+ if (clstr.superClass != null) { // null iff java/lang/Object
+ StructClass clparent = classes.get(clstr.superClass.getString());
+
+ if (clparent != null) {
+ stack.add(clparent);
+ stackSubnodes.add(node);
+ found_parent = true;
+ }
+ }
+ }
+
+ if (!found_parent) { // no super class or interface
+ (isInterface ? rootInterfaces : rootClasses).add(node);
+ }
+ }
+ }
+ }
+
+ this.rootClasses = rootClasses;
+ this.rootInterfaces = rootInterfaces;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/renamer/PoolInterceptor.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/renamer/PoolInterceptor.java
new file mode 100644
index 000000000000..74e2ed8e6fd3
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/renamer/PoolInterceptor.java
@@ -0,0 +1,50 @@
+/*
+ * 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.modules.renamer;
+
+import org.jetbrains.java.decompiler.main.extern.IIdentifierRenamer;
+
+import java.util.HashMap;
+
+public class PoolInterceptor {
+
+ private IIdentifierRenamer helper;
+
+ private HashMap<String, String> mapOldToNewNames = new HashMap<String, String>();
+
+ private HashMap<String, String> mapNewToOldNames = new HashMap<String, String>();
+
+ public PoolInterceptor(IIdentifierRenamer helper) {
+ this.helper = helper;
+ }
+
+ public void addName(String oldName, String newName) {
+ mapOldToNewNames.put(oldName, newName);
+ mapNewToOldNames.put(newName, oldName);
+ }
+
+ public String getName(String oldName) {
+ return mapOldToNewNames.get(oldName);
+ }
+
+ public String getOldName(String newName) {
+ return mapNewToOldNames.get(newName);
+ }
+
+ public IIdentifierRenamer getHelper() {
+ return helper;
+ }
+}