Java tutorial
/* * The MIT License (MIT) * * Copyright (c) SpongePowered <https://www.spongepowered.org> * Copyright (c) contributors * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package org.spongepowered.despector.ast.io.insn; import static org.objectweb.asm.Opcodes.*; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Queues; import com.google.common.collect.Sets; import org.objectweb.asm.Label; import org.objectweb.asm.Type; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.FieldInsnNode; import org.objectweb.asm.tree.FrameNode; import org.objectweb.asm.tree.IincInsnNode; import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.IntInsnNode; import org.objectweb.asm.tree.JumpInsnNode; import org.objectweb.asm.tree.LabelNode; import org.objectweb.asm.tree.LdcInsnNode; import org.objectweb.asm.tree.LineNumberNode; import org.objectweb.asm.tree.LookupSwitchInsnNode; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.TableSwitchInsnNode; import org.objectweb.asm.tree.TypeInsnNode; import org.objectweb.asm.tree.VarInsnNode; import org.spongepowered.despector.ast.io.insn.IntermediateOpcode.AbstractSwitch; import org.spongepowered.despector.ast.io.insn.IntermediateOpcode.DummyInstruction; import org.spongepowered.despector.ast.io.insn.IntermediateOpcode.IntermediateCompareJump; import org.spongepowered.despector.ast.io.insn.IntermediateOpcode.IntermediateConditionalJump; import org.spongepowered.despector.ast.io.insn.IntermediateOpcode.IntermediateFrame; import org.spongepowered.despector.ast.io.insn.IntermediateOpcode.IntermediateGoto; import org.spongepowered.despector.ast.io.insn.IntermediateOpcode.IntermediateJump; import org.spongepowered.despector.ast.io.insn.IntermediateOpcode.IntermediateLabel; import org.spongepowered.despector.ast.io.insn.IntermediateOpcode.IntermediateLookupSwitch; import org.spongepowered.despector.ast.io.insn.IntermediateOpcode.IntermediateStackValue; import org.spongepowered.despector.ast.io.insn.IntermediateOpcode.IntermediateStatement; import org.spongepowered.despector.ast.io.insn.IntermediateOpcode.IntermediateTableSwitch; import org.spongepowered.despector.ast.io.insn.Locals.Local; import org.spongepowered.despector.ast.members.insn.Statement; import org.spongepowered.despector.ast.members.insn.StatementBlock; import org.spongepowered.despector.ast.members.insn.arg.CastArg; import org.spongepowered.despector.ast.members.insn.arg.InstanceFunctionArg; import org.spongepowered.despector.ast.members.insn.arg.InstanceOfArg; import org.spongepowered.despector.ast.members.insn.arg.Instruction; import org.spongepowered.despector.ast.members.insn.arg.NewArrayArg; import org.spongepowered.despector.ast.members.insn.arg.NewRefArg; import org.spongepowered.despector.ast.members.insn.arg.StaticFunctionArg; import org.spongepowered.despector.ast.members.insn.arg.cst.DoubleConstantArg; import org.spongepowered.despector.ast.members.insn.arg.cst.FloatConstantArg; import org.spongepowered.despector.ast.members.insn.arg.cst.IntConstantArg; import org.spongepowered.despector.ast.members.insn.arg.cst.LongConstantArg; import org.spongepowered.despector.ast.members.insn.arg.cst.NullConstantArg; import org.spongepowered.despector.ast.members.insn.arg.cst.StringConstantArg; import org.spongepowered.despector.ast.members.insn.arg.cst.TypeConstantArg; import org.spongepowered.despector.ast.members.insn.arg.field.ArrayLoadArg; import org.spongepowered.despector.ast.members.insn.arg.field.FieldArg; import org.spongepowered.despector.ast.members.insn.arg.field.InstanceFieldArg; import org.spongepowered.despector.ast.members.insn.arg.field.LocalArg; import org.spongepowered.despector.ast.members.insn.arg.field.StaticFieldArg; import org.spongepowered.despector.ast.members.insn.arg.operator.AddArg; import org.spongepowered.despector.ast.members.insn.arg.operator.DivideArg; import org.spongepowered.despector.ast.members.insn.arg.operator.MultiplyArg; import org.spongepowered.despector.ast.members.insn.arg.operator.OperatorArg; import org.spongepowered.despector.ast.members.insn.arg.operator.RemainerArg; import org.spongepowered.despector.ast.members.insn.arg.operator.ShiftLeftArg; import org.spongepowered.despector.ast.members.insn.arg.operator.ShiftRightArg; import org.spongepowered.despector.ast.members.insn.arg.operator.SubtractArg; import org.spongepowered.despector.ast.members.insn.arg.operator.UnsignedShiftRightArg; import org.spongepowered.despector.ast.members.insn.arg.operator.bitwise.AndArg; import org.spongepowered.despector.ast.members.insn.arg.operator.bitwise.OrArg; import org.spongepowered.despector.ast.members.insn.arg.operator.bitwise.XorArg; import org.spongepowered.despector.ast.members.insn.assign.ArrayAssign; import org.spongepowered.despector.ast.members.insn.assign.Assignment; import org.spongepowered.despector.ast.members.insn.assign.FieldAssign; import org.spongepowered.despector.ast.members.insn.assign.InstanceFieldAssign; import org.spongepowered.despector.ast.members.insn.assign.LocalAssign; import org.spongepowered.despector.ast.members.insn.assign.StaticFieldAssign; import org.spongepowered.despector.ast.members.insn.branch.DoWhileLoop; import org.spongepowered.despector.ast.members.insn.branch.ElseBlock; import org.spongepowered.despector.ast.members.insn.branch.ForLoop; import org.spongepowered.despector.ast.members.insn.branch.IfBlock; import org.spongepowered.despector.ast.members.insn.branch.TableSwitch; import org.spongepowered.despector.ast.members.insn.branch.TableSwitch.Case; import org.spongepowered.despector.ast.members.insn.branch.Ternary; import org.spongepowered.despector.ast.members.insn.branch.WhileLoop; import org.spongepowered.despector.ast.members.insn.branch.condition.AndCondition; import org.spongepowered.despector.ast.members.insn.branch.condition.BooleanCondition; import org.spongepowered.despector.ast.members.insn.branch.condition.CompareCondition; import org.spongepowered.despector.ast.members.insn.branch.condition.CompareCondition.CompareOp; import org.spongepowered.despector.ast.members.insn.branch.condition.Condition; import org.spongepowered.despector.ast.members.insn.branch.condition.InverseCondition; import org.spongepowered.despector.ast.members.insn.branch.condition.OrCondition; import org.spongepowered.despector.ast.members.insn.function.InstanceMethodCall; import org.spongepowered.despector.ast.members.insn.function.NewInstance; import org.spongepowered.despector.ast.members.insn.function.StaticMethodCall; import org.spongepowered.despector.ast.members.insn.misc.IncrementStatement; import org.spongepowered.despector.ast.members.insn.misc.ReturnValue; import org.spongepowered.despector.ast.members.insn.misc.ReturnVoid; import org.spongepowered.despector.ast.members.insn.misc.ThrowException; import org.spongepowered.despector.util.AstUtil; import org.spongepowered.despector.util.TypeHelper; import java.util.Deque; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.BiFunction; @SuppressWarnings("unchecked") public class OpcodeDecompiler { private Deque<Instruction> stack; private Locals locals; private List<AbstractInsnNode> instructions; private int instructions_index; private List<IntermediateOpcode> intermediates; private Map<Label, Integer> label_indices; public OpcodeDecompiler() { } public StatementBlock decompile(InsnList instructions, Locals locals) { this.stack = Queues.newArrayDeque(); this.locals = locals; this.intermediates = Lists.newArrayList(); buildIntermediates(instructions, locals); this.label_indices = calcLabelIndices(); return buildBlock(StatementBlock.Type.METHOD, 0, this.intermediates.size()); } private static boolean references(Statement insn, Local local) { if (insn == null) { return false; } if (insn instanceof LocalAssign) { return ((LocalAssign) insn).getLocal() == local; } else if (insn instanceof IncrementStatement) { return ((IncrementStatement) insn).getLocal() == local; } return false; } private static boolean references(Condition condition, Local local) { if (condition instanceof BooleanCondition) { return references(((BooleanCondition) condition).getConditionValue(), local); } else if (condition instanceof CompareCondition) { return references(((CompareCondition) condition).getLeft(), local) || references(((CompareCondition) condition).getRight(), local); } return false; } private static boolean references(Instruction arg, Local local) { if (arg instanceof LocalArg) { return ((LocalArg) arg).getLocal() == local; } return false; } private StatementBlock buildBlock(StatementBlock.Type type, int start, int end) { StatementBlock block = new StatementBlock(type, this.locals); Ternary tmp_ternary = null; for (int index = start; index < end; index++) { IntermediateOpcode next = this.intermediates.get(index); if (next instanceof IntermediateStatement) { Statement stmt = ((IntermediateStatement) next).getStatement(); if (tmp_ternary != null) { if (stmt instanceof Assignment) { ((Assignment) stmt).setValue(tmp_ternary); tmp_ternary = null; } else if (stmt instanceof ReturnValue) { ((ReturnValue) stmt).setValue(tmp_ternary); tmp_ternary = null; } } block.append(stmt); } else if (next instanceof IntermediateStackValue) { block.append((IntermediateStackValue) next); } else if (next instanceof AbstractSwitch) { AbstractSwitch aswitch = (AbstractSwitch) next; Instruction var = aswitch.getSwitchVar(); TableSwitch tswitch = new TableSwitch(var); boolean added_dflt = false; List<LabelNode> labels = aswitch.getLabels(); if (!labels.contains(aswitch.getDefault())) { labels.add(aswitch.getDefault()); added_dflt = true; } int last = -1; LabelNode break_label = null; LabelNode last_label = null; int switch_end = index; for (int i = index + 1; i < end; i++) { IntermediateOpcode cnext = this.intermediates.get(i); if (cnext instanceof IntermediateLabel) { if (labels.contains(((IntermediateLabel) cnext).getLabel())) { if (last == -1) { last = i; last_label = ((IntermediateLabel) cnext).getLabel(); continue; } IntermediateOpcode last_op = this.intermediates.get(i - 1); boolean breaks = false; if (last_op instanceof IntermediateGoto) { LabelNode label = ((IntermediateGoto) last_op).getNode().label; if (this.label_indices.get(label.getLabel()) > i) { breaks = true; break_label = label; } } int case_end = i; if (breaks) { case_end--; } boolean is_def = aswitch.getDefault() == last_label; List<Integer> indices = Lists.newArrayList(); for (LabelNode l : labels) { if (l == last_label) { indices.add(aswitch.indexFor(l)); } } StatementBlock body_block = buildBlock(StatementBlock.Type.SWITCH, last, case_end); tswitch.addCase(new Case(body_block, breaks, is_def, indices)); last = i; last_label = ((IntermediateLabel) cnext).getLabel(); int case_index = labels.lastIndexOf(((IntermediateLabel) cnext).getLabel()); if (case_index == labels.size() - 1) { is_def = aswitch.getDefault() == last_label; indices = Lists.newArrayList(); for (LabelNode l : labels) { if (l == last_label) { if (l == aswitch.getDefault() && added_dflt) { continue; } indices.add(aswitch.indexFor(l)); } } if (break_label != null) { int last_end = this.label_indices.get(break_label.getLabel()); last_op = this.intermediates.get(last_end - 1); breaks = false; if (last_op instanceof IntermediateGoto) { LabelNode label = ((IntermediateGoto) last_op).getNode().label; if (this.label_indices.get(label.getLabel()) > i) { breaks = true; } } if (breaks) { last_end--; } StatementBlock last_block = buildBlock(StatementBlock.Type.SWITCH, i, last_end); tswitch.addCase(new Case(last_block, false, is_def, indices)); switch_end = last_end; if (breaks) { switch_end++; } } else { for (int o = i; o < end; o++) { IntermediateOpcode onext = this.intermediates.get(o); if (onext instanceof IntermediateFrame) { case_end = o; break; } } int last_end = case_end; last_op = this.intermediates.get(last_end - 1); breaks = false; if (last_op instanceof IntermediateGoto) { LabelNode label = ((IntermediateGoto) last_op).getNode().label; if (this.label_indices.get(label.getLabel()) > i) { breaks = true; } } if (breaks) { last_end--; } StatementBlock last_block = buildBlock(StatementBlock.Type.SWITCH, i, last_end); tswitch.addCase(new Case(last_block, false, is_def, indices)); switch_end = case_end; } } } } } block.append(tswitch); index = switch_end; } else if (next instanceof IntermediateJump) { if (next instanceof IntermediateGoto) { if (index == end - 1 && type == StatementBlock.Type.IF) { break; } int target = this.label_indices.get(((IntermediateJump) next).getNode().label.getLabel()); ConditionResult result = makeCondition(target); StatementBlock body_block = buildBlock(StatementBlock.Type.WHILE, index + 1, target); if (!block.getStatements().isEmpty()) { Statement init = block.getStatements().get(block.getStatements().size() - 1); Statement incr = null; if (!body_block.getStatements().isEmpty()) { incr = body_block.getStatements().get(body_block.getStatements().size() - 1); } if (init instanceof LocalAssign) { Local local = ((LocalAssign) init).getLocal(); if (references(result.condition, local) || references(incr, local)) { block.getStatements().remove(init); if (references(incr, local)) { body_block.getStatements().remove(incr); ForLoop forloop = new ForLoop(init, new InverseCondition(result.condition), incr, body_block); block.append(forloop); index = result.end; continue; } ForLoop forloop = new ForLoop(init, new InverseCondition(result.condition), null, body_block); block.append(forloop); index = result.end; continue; } } } WhileLoop whileloop = new WhileLoop(new InverseCondition(result.condition), body_block); block.append(whileloop); index = result.end; continue; } ConditionResult result = makeCondition(index); if (result.block_end < index) { StatementBlock body_block = buildBlock(StatementBlock.Type.WHILE, result.block_end, index - 1); block.getStatements().removeAll(body_block.getStatements()); DoWhileLoop dowhile = new DoWhileLoop(new InverseCondition(result.condition), body_block); block.append(dowhile); index = result.end; } else { StatementBlock body_block = buildBlock(StatementBlock.Type.IF, result.end, result.block_end); boolean is_ternary = false; Instruction true_val = null; Instruction false_val = null; if (body_block.getStatements().get(0) instanceof IntermediateStackValue) { is_ternary = true; true_val = ((IntermediateStackValue) body_block.getStatements().get(0)).getStackVal(); } IfBlock if_block = new IfBlock(result.condition, body_block); IntermediateOpcode last = this.intermediates.get(result.block_end - 1); if (last instanceof IntermediateGoto) { int target = this.label_indices.get(((IntermediateGoto) last).getNode().label.getLabel()); StatementBlock else_block = buildBlock(StatementBlock.Type.IF, result.block_end + 1, target); if (is_ternary) { if (!(else_block.getStatements().get(0) instanceof IntermediateStackValue)) { throw new IllegalStateException(); } false_val = ((IntermediateStackValue) else_block.getStatements().get(0)).getStackVal(); } ElseBlock elseblock = new ElseBlock(else_block); if_block.setElseBlock(elseblock); result.block_end = target; } if (is_ternary) { if (false_val == null || true_val == null) { throw new IllegalStateException(); } Ternary ternary = new Ternary(result.condition, true_val, false_val); tmp_ternary = ternary; } else { block.append(if_block); } index = result.block_end; } } } return block; } private static enum CompareOps { AND, OR, MID, MID_OR } static class ConditionResult { public Condition condition; int end; int block_end; } private ConditionResult makeCondition(int index) { int condition_start = index; int condition_end = index; for (; condition_end < this.intermediates.size(); condition_end++) { IntermediateOpcode onext = this.intermediates.get(condition_end); if (onext instanceof IntermediateStatement || onext instanceof IntermediateGoto || onext instanceof IntermediateStackValue) { break; } } Set<LabelNode> seen_labels = Sets.newHashSet(); List<IntermediateOpcode> group = Lists.newArrayList(); int farthest = 0; for (int i = condition_start; i < condition_end; i++) { IntermediateOpcode cnext = this.intermediates.get(i); if (cnext instanceof IntermediateLabel) { if (i == condition_end - 1) { break; } boolean sharing = false; for (int o = i + 1; o < condition_end; o++) { IntermediateOpcode onext = this.intermediates.get(o); if (onext instanceof IntermediateJump) { LabelNode label = ((IntermediateJump) onext).getNode().label; if (seen_labels.contains(label)) { sharing = true; break; } if (this.label_indices.get(label.getLabel()) > farthest) { sharing = true; break; } } else if (onext instanceof IntermediateLabel) { break; } } if (!sharing) { for (int c = condition_start; c < i; c++) { group.add(this.intermediates.get(c)); } condition_end = i; farthest = -1; break; } } else if (cnext instanceof IntermediateJump) { LabelNode label = ((IntermediateJump) cnext).getNode().label; seen_labels.add(label); int target = this.label_indices.get(label.getLabel()); if (target > farthest) { farthest = target; } } } if (farthest != -1) { for (int c = condition_start; c < condition_end; c++) { group.add(this.intermediates.get(c)); } } for (int c = group.size() - 1; c >= 0; c--) { if (group.get(c) instanceof IntermediateJump) { break; } group.remove(group.get(c)); } Condition condition = makeCondition(group); LabelNode break_node = ((IntermediateJump) group.get(group.size() - 1)).getNode().label; int block_end = this.label_indices.get(break_node.getLabel()); ConditionResult result = new ConditionResult(); result.condition = condition; result.end = condition_end; result.block_end = block_end; return result; } private Condition makeCondition(List<IntermediateOpcode> group) { int start = this.intermediates.indexOf(group.get(0)); IntermediateOpcode last = group.get(group.size() - 1); int end = this.intermediates.indexOf(last); LabelNode break_node = ((IntermediateJump) group.get(group.size() - 1)).getNode().label; Deque<Condition> stack = Queues.newArrayDeque(); Deque<CompareOps> ops_stack = Queues.newArrayDeque(); for (int i = 0; i < group.size(); i++) { IntermediateOpcode next = group.get(i); if (next instanceof IntermediateJump) { JumpInsnNode node = ((IntermediateJump) next).getNode(); if (node.label == break_node) { if (next instanceof IntermediateConditionalJump) { if (node.getOpcode() == IFEQ) { stack.push(new BooleanCondition(((IntermediateConditionalJump) next).getCondition(), false)); } else if (node.getOpcode() == IFNE) { stack.push(new BooleanCondition(((IntermediateConditionalJump) next).getCondition(), true)); } else if (node.getOpcode() == IFNULL) { stack.push(new CompareCondition(((IntermediateConditionalJump) next).getCondition(), new NullConstantArg(), CompareOp.NOT_EQUAL)); } else if (node.getOpcode() == IFNONNULL) { stack.push(new CompareCondition(((IntermediateConditionalJump) next).getCondition(), new NullConstantArg(), CompareOp.EQUAL)); } else { stack.push(new CompareCondition(((IntermediateConditionalJump) next).getCondition(), new IntConstantArg(0), CompareCondition.fromOpcode(node.getOpcode()).inverse())); } } else { IntermediateCompareJump cmp = (IntermediateCompareJump) next; stack.push(new CompareCondition(cmp.getLeft(), cmp.getRight(), CompareCondition.fromOpcode(node.getOpcode()).inverse())); } if (!ops_stack.isEmpty()) { CompareOps op = ops_stack.peek(); if (op == CompareOps.AND) { ops_stack.pop(); Condition left = stack.pop(); Condition right = stack.pop(); AndCondition cond = new AndCondition(right, left); stack.push(cond); } if (i < group.size() - 1) { if (group.get(i + 1) instanceof IntermediateLabel) { CompareOps nop = ops_stack.peek(); if (nop == CompareOps.OR || nop == CompareOps.MID) { ops_stack.pop(); Condition left = stack.pop(); Condition right = new InverseCondition(stack.pop()); OrCondition cond = new OrCondition(right, left); stack.push(cond); ops_stack.push(CompareOps.AND); } } else { ops_stack.push(CompareOps.AND); } } } else if (i < group.size() - 1) { ops_stack.push(CompareOps.AND); } } else { int jump_target = this.label_indices.get(node.label.getLabel()); if (jump_target <= end && jump_target >= start) { if (next instanceof IntermediateConditionalJump) { if (node.getOpcode() == IFEQ) { stack.push(new BooleanCondition(((IntermediateConditionalJump) next).getCondition(), false)); } else if (node.getOpcode() == IFNE) { stack.push(new BooleanCondition(((IntermediateConditionalJump) next).getCondition(), true)); } else if (node.getOpcode() == IFNULL) { stack.push(new CompareCondition(((IntermediateConditionalJump) next).getCondition(), new NullConstantArg(), CompareOp.NOT_EQUAL)); } else if (node.getOpcode() == IFNONNULL) { stack.push(new CompareCondition(((IntermediateConditionalJump) next).getCondition(), new NullConstantArg(), CompareOp.EQUAL)); } else { stack.push(new CompareCondition(((IntermediateConditionalJump) next).getCondition(), new IntConstantArg(0), CompareCondition.fromOpcode(node.getOpcode()).inverse())); } } else { IntermediateCompareJump cmp = (IntermediateCompareJump) next; stack.push(new CompareCondition(cmp.getLeft(), cmp.getRight(), CompareCondition.fromOpcode(node.getOpcode()).inverse())); } if (ops_stack.peek() == CompareOps.MID) { ops_stack.pop(); Condition left = stack.pop(); Condition right = new InverseCondition(stack.pop()); OrCondition cond = new OrCondition(right, left); stack.push(cond); } ops_stack.push(CompareOps.MID); } else { if (next instanceof IntermediateConditionalJump) { if (node.getOpcode() == IFEQ) { stack.push(new BooleanCondition(((IntermediateConditionalJump) next).getCondition(), true)); } else if (node.getOpcode() == IFNE) { stack.push(new BooleanCondition(((IntermediateConditionalJump) next).getCondition(), false)); } else if (node.getOpcode() == IFNULL) { stack.push(new CompareCondition(((IntermediateConditionalJump) next).getCondition(), new NullConstantArg(), CompareOp.EQUAL)); } else if (node.getOpcode() == IFNONNULL) { stack.push(new CompareCondition(((IntermediateConditionalJump) next).getCondition(), new NullConstantArg(), CompareOp.NOT_EQUAL)); } else { stack.push(new CompareCondition(((IntermediateConditionalJump) next).getCondition(), new IntConstantArg(0), CompareCondition.fromOpcode(node.getOpcode()))); } } else { IntermediateCompareJump cmp = (IntermediateCompareJump) next; stack.push(new CompareCondition(cmp.getLeft(), cmp.getRight(), CompareCondition.fromOpcode(node.getOpcode()))); } if (ops_stack.peek() == CompareOps.MID) { if (i < group.size() - 1 && group.get(i + 1) instanceof IntermediateLabel) { ops_stack.pop(); Condition left = stack.pop(); Condition right = stack.pop(); AndCondition cond = new AndCondition(right, left); stack.push(cond); } else { ops_stack.pop(); ops_stack.push(CompareOps.MID_OR); } } else if (ops_stack.peek() == CompareOps.MID_OR) { ops_stack.pop(); Condition left = stack.pop(); Condition right = stack.pop(); OrCondition cond = new OrCondition(right, left); Condition right2 = stack.pop(); AndCondition cond2 = new AndCondition(right2, cond); stack.push(cond2); } else { ops_stack.push(CompareOps.OR); } } } } } while (!ops_stack.isEmpty()) { Condition left = stack.pop(); Condition right = stack.pop(); CompareOps op = ops_stack.pop(); if (op == CompareOps.AND) { AndCondition cond = new AndCondition(right, left); stack.push(cond); } else if (op == CompareOps.OR) { OrCondition cond = new OrCondition(right, left); stack.push(cond); } } if (stack.size() == 2) { Condition left = stack.pop(); Condition right = stack.pop(); OrCondition cond = new OrCondition(right, left); stack.push(cond); } return stack.pop(); } private static boolean intermediate_stack = false; private void handleIntermediate(AbstractInsnNode next) { if (next instanceof JumpInsnNode) { if (next.getOpcode() == GOTO) { if (!this.stack.isEmpty()) { intermediate_stack = true; this.intermediates.add(new IntermediateStackValue(this.stack.pop())); } this.intermediates.add(new IntermediateGoto((JumpInsnNode) next)); } else if (next.getOpcode() == IF_ACMPEQ || next.getOpcode() == IF_ACMPNE || next.getOpcode() == IF_ICMPEQ || next.getOpcode() == IF_ICMPGE || next.getOpcode() == IF_ICMPGT || next.getOpcode() == IF_ICMPLE || next.getOpcode() == IF_ICMPLT || next.getOpcode() == IF_ICMPNE) { Instruction right = this.stack.pop(); Instruction left = this.stack.pop(); this.intermediates.add(new IntermediateCompareJump((JumpInsnNode) next, left, right)); } else { Instruction condition = this.stack.pop(); this.intermediates.add(new IntermediateConditionalJump((JumpInsnNode) next, condition)); } } else if (next instanceof TableSwitchInsnNode) { this.intermediates.add(new IntermediateTableSwitch((TableSwitchInsnNode) next, this.stack.pop())); } else if (next instanceof LookupSwitchInsnNode) { this.intermediates.add(new IntermediateLookupSwitch((LookupSwitchInsnNode) next, this.stack.pop())); } else if (next instanceof LineNumberNode) { } else if (next instanceof LabelNode) { this.intermediates.add(new IntermediateLabel((LabelNode) next)); } else if (next instanceof FrameNode) { if (!this.stack.isEmpty()) { this.intermediates.add(this.intermediates.size() - 1, new IntermediateStackValue(this.stack.pop())); this.stack.push(new DummyInstruction()); intermediate_stack = false; } this.intermediates.add(new IntermediateFrame((FrameNode) next)); } else { if (intermediate_stack && !this.stack.isEmpty() && next.getOpcode() >= IRETURN && next.getOpcode() <= ARETURN) { this.intermediates.add(this.intermediates.size() - 1, new IntermediateStackValue(this.stack.pop())); this.stack.push(new DummyInstruction()); intermediate_stack = false; } OpHandler handle = handlers[next.getOpcode()]; if (handle == null) { System.err.println("Unsupported opcode " + next.getOpcode()); throw new IllegalStateException(); } handle.handle(this, next); } } private void buildIntermediates(InsnList instructions, Locals locals) { this.instructions = Lists.newArrayList(); Iterator<AbstractInsnNode> it = instructions.iterator(); while (it.hasNext()) { AbstractInsnNode next = it.next(); this.instructions.add(next); } intermediate_stack = false; for (this.instructions_index = 0; this.instructions_index < this.instructions.size();) { AbstractInsnNode next = this.instructions.get(this.instructions_index++); // System.out.println(AstUtil.insnToString(next)); handleIntermediate(next); } } private Map<Label, Integer> calcLabelIndices() { Map<Label, Integer> indices = Maps.newHashMap(); int i = 0; for (IntermediateOpcode next : this.intermediates) { if (next instanceof IntermediateOpcode.IntermediateLabel) { indices.put(((IntermediateOpcode.IntermediateLabel) next).getLabel().getLabel(), i); } i++; } return indices; } public void push(Instruction arg) { this.stack.push(arg); } public Instruction pop() { return this.stack.pop(); } public Instruction peek() { return this.stack.peek(); } public void append(Statement statement) { this.intermediates.add(new IntermediateOpcode.IntermediateStatement(statement)); } public Locals.Local getLocal(int index) { return this.locals.getLocal(index); } public AbstractInsnNode next() { AbstractInsnNode next = this.instructions.get(this.instructions_index++); // System.out.println(AstUtil.insnToString(next)); return next; } public void revert(int n) { // System.out.println("Back up " + n); this.instructions_index -= n; } /** * A handler for a single opcode. */ @FunctionalInterface private static interface OpHandler { void handle(OpcodeDecompiler state, AbstractInsnNode next); } // Highest opcode is IFNONNULL at 199 private static final int MAX_OPCODE = 200; private static final OpHandler[] handlers = new OpHandler[MAX_OPCODE]; static { OpHandler noop = (state, next) -> { }; handlers[NOP] = noop; // Constants handlers[ACONST_NULL] = (state, next) -> { state.push(new NullConstantArg()); }; handlers[ICONST_M1] = (state, next) -> { state.push(new IntConstantArg(-1)); }; handlers[ICONST_0] = (state, next) -> { state.push(new IntConstantArg(0)); }; handlers[ICONST_1] = (state, next) -> { state.push(new IntConstantArg(1)); }; handlers[ICONST_2] = (state, next) -> { state.push(new IntConstantArg(2)); }; handlers[ICONST_3] = (state, next) -> { state.push(new IntConstantArg(3)); }; handlers[ICONST_4] = (state, next) -> { state.push(new IntConstantArg(4)); }; handlers[ICONST_5] = (state, next) -> { state.push(new IntConstantArg(5)); }; handlers[LCONST_0] = (state, next) -> { state.push(new LongConstantArg(0)); }; handlers[LCONST_1] = (state, next) -> { state.push(new LongConstantArg(1)); }; handlers[FCONST_0] = (state, next) -> { state.push(new FloatConstantArg(0)); }; handlers[FCONST_1] = (state, next) -> { state.push(new FloatConstantArg(1)); }; handlers[FCONST_2] = (state, next) -> { state.push(new FloatConstantArg(2)); }; handlers[DCONST_0] = (state, next) -> { state.push(new DoubleConstantArg(0)); }; handlers[DCONST_1] = (state, next) -> { state.push(new DoubleConstantArg(1)); }; OpHandler int_constant = (state, next) -> { IntInsnNode val = (IntInsnNode) next; IntConstantArg arg = new IntConstantArg(val.operand); state.push(arg); }; handlers[BIPUSH] = int_constant; handlers[SIPUSH] = int_constant; handlers[LDC] = (state, next) -> { LdcInsnNode ldc = (LdcInsnNode) next; if (ldc.cst instanceof String) { state.push(new StringConstantArg((String) ldc.cst)); } else if (ldc.cst instanceof Integer) { state.push(new IntConstantArg((Integer) ldc.cst)); } else if (ldc.cst instanceof Float) { state.push(new FloatConstantArg((Float) ldc.cst)); } else if (ldc.cst instanceof Long) { // LDC_W appears to be merged with this opcode by asm so long // and double constants will also be here state.push(new LongConstantArg((Long) ldc.cst)); } else if (ldc.cst instanceof Double) { state.push(new DoubleConstantArg((Double) ldc.cst)); } else if (ldc.cst instanceof Type) { state.push(new TypeConstantArg((Type) ldc.cst)); } else { throw new IllegalStateException("Unsupported ldc constant: " + ldc.cst); } }; // Local variable load/store OpHandler local_load = (state, next) -> { VarInsnNode var = (VarInsnNode) next; Local local = state.getLocal(var.var); if (local.getName() == null) { // if the local has no name defined we give it a simple name. local.setName("local" + local.getIndex()); } LocalArg arg = new LocalArg(local); state.push(arg); }; handlers[ILOAD] = local_load; handlers[LLOAD] = local_load; handlers[FLOAD] = local_load; handlers[DLOAD] = local_load; handlers[ALOAD] = local_load; OpHandler array_load = (state, next) -> { Instruction index = state.pop(); Instruction var = state.pop(); ArrayLoadArg load = new ArrayLoadArg(var, index); state.push(load); }; handlers[IALOAD] = array_load; handlers[LALOAD] = array_load; handlers[FALOAD] = array_load; handlers[DALOAD] = array_load; handlers[AALOAD] = array_load; handlers[BALOAD] = array_load; handlers[CALOAD] = array_load; handlers[SALOAD] = array_load; OpHandler local_store = (state, next) -> { VarInsnNode var = (VarInsnNode) next; Instruction val = state.pop(); Local local = state.getLocal(var.var); local.set(val); LocalAssign insn = new LocalAssign(local, val); state.append(insn); }; handlers[ISTORE] = local_store; handlers[LSTORE] = local_store; handlers[FSTORE] = local_store; handlers[DSTORE] = local_store; handlers[ASTORE] = local_store; OpHandler array_store = (state, next) -> { Instruction val = state.pop(); Instruction index = state.pop(); Instruction var = state.pop(); ArrayAssign assign = new ArrayAssign(var, index, val); state.append(assign); }; handlers[IASTORE] = array_store; handlers[LASTORE] = array_store; handlers[FASTORE] = array_store; handlers[DASTORE] = array_store; handlers[AASTORE] = array_store; handlers[BASTORE] = array_store; handlers[CASTORE] = array_store; handlers[SASTORE] = array_store; handlers[POP] = (state, next) -> { Instruction arg = state.pop(); if (arg instanceof InstanceFunctionArg) { state.append(new InstanceMethodCall((InstanceFunctionArg) arg)); } else if (arg instanceof StaticFunctionArg) { state.append(new StaticMethodCall((StaticFunctionArg) arg)); } }; handlers[POP2] = (state, next) -> { Instruction arg = state.pop(); if (arg instanceof InstanceFunctionArg) { state.append(new InstanceMethodCall((InstanceFunctionArg) arg)); } else if (arg instanceof StaticFunctionArg) { state.append(new StaticMethodCall((StaticFunctionArg) arg)); } else { throw new IllegalStateException("Unknown arg being popped: " + arg); } arg = state.pop(); if (arg instanceof InstanceFunctionArg) { state.append(new InstanceMethodCall((InstanceFunctionArg) arg)); } else if (arg instanceof StaticFunctionArg) { state.append(new StaticMethodCall((StaticFunctionArg) arg)); } else { throw new IllegalStateException("Unknown arg being popped: " + arg); } }; // Stack manipulation handlers[DUP] = (state, next) -> { state.push(state.peek()); }; handlers[DUP_X1] = (state, next) -> { Instruction val = state.pop(); Instruction val2 = state.pop(); state.push(val); state.push(val2); state.push(val); }; handlers[DUP_X2] = (state, next) -> { Instruction val = state.pop(); Instruction val2 = state.pop(); Instruction val3 = state.pop(); state.push(val); state.push(val3); state.push(val2); state.push(val); }; handlers[DUP2] = (state, next) -> { Instruction val = state.pop(); Instruction val2 = state.peek(); state.push(val); state.push(val2); state.push(val); }; handlers[DUP2_X1] = (state, next) -> { Instruction val = state.pop(); Instruction val2 = state.pop(); Instruction val3 = state.pop(); state.push(val2); state.push(val); state.push(val3); state.push(val2); state.push(val); }; handlers[DUP2_X2] = (state, next) -> { Instruction val = state.pop(); Instruction val2 = state.pop(); Instruction val3 = state.pop(); Instruction val4 = state.pop(); state.push(val2); state.push(val); state.push(val4); state.push(val3); state.push(val2); state.push(val); }; handlers[SWAP] = (state, next) -> { Instruction val = state.pop(); Instruction val2 = state.pop(); state.push(val); state.push(val2); }; // Operators handlers[IADD] = new OperatorHandler(AddArg::new); handlers[LADD] = new OperatorHandler(AddArg::new); handlers[FADD] = new OperatorHandler(AddArg::new); handlers[DADD] = new OperatorHandler(AddArg::new); handlers[ISUB] = new OperatorHandler(SubtractArg::new); handlers[LSUB] = new OperatorHandler(SubtractArg::new); handlers[FSUB] = new OperatorHandler(SubtractArg::new); handlers[DSUB] = new OperatorHandler(SubtractArg::new); handlers[IMUL] = new OperatorHandler(MultiplyArg::new); handlers[LMUL] = new OperatorHandler(MultiplyArg::new); handlers[FMUL] = new OperatorHandler(MultiplyArg::new); handlers[DMUL] = new OperatorHandler(MultiplyArg::new); handlers[IDIV] = new OperatorHandler(DivideArg::new); handlers[LDIV] = new OperatorHandler(DivideArg::new); handlers[FDIV] = new OperatorHandler(DivideArg::new); handlers[DDIV] = new OperatorHandler(DivideArg::new); handlers[IREM] = new OperatorHandler(RemainerArg::new); handlers[LREM] = new OperatorHandler(RemainerArg::new); handlers[FREM] = new OperatorHandler(RemainerArg::new); handlers[DREM] = new OperatorHandler(RemainerArg::new); handlers[ISHL] = new OperatorHandler(ShiftLeftArg::new); handlers[LSHL] = new OperatorHandler(ShiftLeftArg::new); handlers[ISHR] = new OperatorHandler(ShiftRightArg::new); handlers[LSHR] = new OperatorHandler(ShiftRightArg::new); handlers[IUSHR] = new OperatorHandler(UnsignedShiftRightArg::new); handlers[LUSHR] = new OperatorHandler(UnsignedShiftRightArg::new); handlers[IAND] = new OperatorHandler(AndArg::new); handlers[LAND] = new OperatorHandler(AndArg::new); handlers[IOR] = new OperatorHandler(OrArg::new); handlers[LOR] = new OperatorHandler(OrArg::new); handlers[IXOR] = new OperatorHandler(XorArg::new); handlers[LXOR] = new OperatorHandler(XorArg::new); handlers[IINC] = (state, next) -> { IincInsnNode inc = (IincInsnNode) next; IncrementStatement insn = new IncrementStatement(state.getLocal(inc.var), inc.incr); state.append(insn); }; // Casting handlers[I2L] = new CastHandler("J"); handlers[I2F] = new CastHandler("F"); handlers[I2D] = new CastHandler("D"); handlers[L2I] = new CastHandler("I"); handlers[L2F] = new CastHandler("F"); handlers[L2D] = new CastHandler("D"); handlers[F2I] = new CastHandler("I"); handlers[F2L] = new CastHandler("J"); handlers[F2D] = new CastHandler("D"); handlers[D2I] = new CastHandler("I"); handlers[D2L] = new CastHandler("J"); handlers[D2F] = new CastHandler("F"); handlers[I2B] = new CastHandler("B"); handlers[I2C] = new CastHandler("C"); handlers[I2S] = new CastHandler("S"); // Jumping handlers[LCMP] = null; // TODO LCMP handlers[FCMPL] = null; // TODO FCMPL handlers[FCMPG] = null; // TODO FCMPG handlers[DCMPL] = null; // TODO DCMPL handlers[DCMPG] = null; // TODO DCMPG handlers[IFEQ] = null; // handled in the post process handlers[IFNE] = null; // -- handlers[IFGE] = null; // -- handlers[IFGT] = null; // -- handlers[IFLT] = null; // -- handlers[IFLE] = null; // -- handlers[IF_ACMPEQ] = null; // -- handlers[IF_ACMPNE] = null; // -- handlers[IF_ICMPEQ] = null; // -- handlers[IF_ICMPNE] = null; // -- handlers[IF_ICMPGE] = null; // -- handlers[IF_ICMPGT] = null; // -- handlers[IF_ICMPLE] = null; // -- handlers[IF_ICMPLT] = null; // -- handlers[GOTO] = null; // -- handlers[JSR] = null; // TODO JSR handlers[RET] = null; // TODO RET handlers[TABLESWITCH] = null; // deferred handling handlers[LOOKUPSWITCH] = null; // deferred handling // Returns OpHandler return_value = (state, next) -> { state.append(new ReturnValue(state.pop())); }; handlers[IRETURN] = return_value; handlers[LRETURN] = return_value; handlers[FRETURN] = return_value; handlers[DRETURN] = return_value; handlers[ARETURN] = return_value; handlers[RETURN] = (state, next) -> { state.append(new ReturnVoid()); }; // Field handling handlers[GETSTATIC] = (state, next) -> { FieldInsnNode field = (FieldInsnNode) next; String owner = field.owner; if (!owner.startsWith("[")) { owner = "L" + owner + ";"; } FieldArg arg = new StaticFieldArg(field.name, field.desc, owner); state.push(arg); }; handlers[PUTSTATIC] = (state, next) -> { FieldInsnNode field = (FieldInsnNode) next; Instruction val = state.pop(); String owner = field.owner; if (!owner.startsWith("[")) { owner = "L" + owner + ";"; } FieldAssign assign = new StaticFieldAssign(field.name, field.desc, owner, val); state.append(assign); }; handlers[GETFIELD] = (state, next) -> { FieldInsnNode field = (FieldInsnNode) next; String owner = field.owner; if (!owner.startsWith("[")) { owner = "L" + owner + ";"; } FieldArg arg = new InstanceFieldArg(field.name, field.desc, owner, state.pop()); state.push(arg); }; handlers[PUTFIELD] = (state, next) -> { FieldInsnNode field = (FieldInsnNode) next; Instruction val = state.pop(); Instruction owner = state.pop(); String owner_t = field.owner; if (!owner_t.startsWith("[")) { owner_t = "L" + owner_t + ";"; } FieldAssign assign = new InstanceFieldAssign(field.name, field.desc, owner_t, owner, val); state.append(assign); }; // Method invocation OpHandler method_invoke = (state, next) -> { MethodInsnNode method = (MethodInsnNode) next; String[] param_types = TypeHelper.splitSig(method.desc); String ret = TypeHelper.getRet(method.desc); Instruction[] args = new Instruction[param_types.length]; for (int i = args.length - 1; i >= 0; i--) { args[i] = state.pop(); } Instruction callee = state.pop(); String owner = method.owner; if (!owner.startsWith("[")) { owner = "L" + owner + ";"; } if (ret.equals("V")) { state.append(new InstanceMethodCall(method.name, method.desc, owner, args, callee)); } else { InstanceFunctionArg arg = new InstanceFunctionArg(method.name, method.desc, owner, args, callee); state.push(arg); } }; handlers[INVOKEVIRTUAL] = method_invoke; handlers[INVOKESPECIAL] = method_invoke; handlers[INVOKESTATIC] = (state, next) -> { MethodInsnNode method = (MethodInsnNode) next; String[] param_types = TypeHelper.splitSig(method.desc); String ret = TypeHelper.getRet(method.desc); Instruction[] args = new Instruction[param_types.length]; for (int i = args.length - 1; i >= 0; i--) { args[i] = state.pop(); } String owner = method.owner; if (!owner.startsWith("[")) { owner = "L" + owner + ";"; } if (ret.equals("V")) { state.append(new StaticMethodCall(method.name, method.desc, owner, args)); } else { StaticFunctionArg arg = new StaticFunctionArg(method.name, method.desc, owner, args); state.push(arg); } }; handlers[INVOKEINTERFACE] = method_invoke; handlers[INVOKEDYNAMIC] = null; // TODO INVOKEDYNAMIC // Type allocation handlers[NEW] = (state, next) -> { String type = ((TypeInsnNode) next).desc; next = state.next(); boolean standalone = next.getOpcode() != DUP; if (standalone) { next = state.next(); } while (true) { next = state.next(); if (next.getOpcode() == INVOKESPECIAL) { MethodInsnNode mth = (MethodInsnNode) next; if ("<init>".equals(mth.name) && type.equals(mth.owner)) { break; } } state.handleIntermediate(next); } MethodInsnNode ctor = (MethodInsnNode) next; String[] ctor_params = TypeHelper.splitSig(ctor.desc); Instruction[] args = new Instruction[ctor_params.length]; for (int i = 0; i < args.length; i++) { args[i] = state.pop(); } if (standalone) { NewInstance insn = new NewInstance("L" + type + ";", ctor.desc, args); state.append(insn); } else { NewRefArg arg = new NewRefArg("L" + type + ";", ctor.desc, args); state.push(arg); } }; OpHandler array_init = (state, next) -> { // There are two forms of array initializers: // // new Type[size] // // and // // new Type[]{val1, val2, val3,..., valN} // // This consumer will handle either of the two form. // The first part of the initializer is the same for either form: // // BIPUSH size // NEWARRAY type // // and then the second form will move to a pattern of // // DUP // BIPUSH index // [...] create value // IASTORE // // Which it will repeat until it finishes and moves into the logic // which // consumes the array Instruction size = state.pop(); int tstore = -1; String array_type = null; if (next instanceof IntInsnNode) { IntInsnNode array = (IntInsnNode) next; tstore = AstUtil.opcodeToStore(array.operand); array_type = AstUtil.opcodeToType(array.operand); } else if (next instanceof TypeInsnNode) { TypeInsnNode array = (TypeInsnNode) next; tstore = AASTORE; array_type = array.desc; } final int store = tstore; next = state.next(); Instruction[] init = null; // TODO // This makes an assumption that the array is always stored to // value, // which is obviously completely wrong. // We should optimistically loop for an initializer pattern and // otherwise just revert and continue on passing the array on the // stack // to the next opcode in the original base consumer. if (next.getOpcode() == DUP && size instanceof IntConstantArg) { init = new Instruction[((IntConstantArg) size).getConstant()]; while (next.getOpcode() == DUP) { next = state.next(); int index = -1; // TODO move this conversion to a utility if (next.getOpcode() == ICONST_0) { index = 0; } else if (next.getOpcode() == ICONST_1) { index = 1; } else if (next.getOpcode() == ICONST_2) { index = 2; } else if (next.getOpcode() == ICONST_3) { index = 3; } else if (next.getOpcode() == ICONST_4) { index = 4; } else if (next.getOpcode() == ICONST_5) { index = 5; } else if (next.getOpcode() == BIPUSH) { index = ((IntInsnNode) next).operand; } else if (next.getOpcode() == SIPUSH) { index = ((IntInsnNode) next).operand; } else { throw new RuntimeException("Unknown array index: " + AstUtil.insnToString(next)); } next = state.next(); while (next.getOpcode() != store) { state.handleIntermediate(next); next = state.next(); } Instruction val = state.pop(); init[index] = val; next = state.next(); } } else { state.revert(1); } NewArrayArg arg = new NewArrayArg(array_type, size, init); state.push(arg); }; handlers[NEWARRAY] = array_init; handlers[ANEWARRAY] = array_init; // Misc handlers[ARRAYLENGTH] = (state, next) -> { FieldArg arg = new InstanceFieldArg("length", "I", null, state.pop()); state.push(arg); }; handlers[ATHROW] = (state, next) -> { state.append(new ThrowException(state.pop())); }; handlers[CHECKCAST] = (state, next) -> { TypeInsnNode cast = (TypeInsnNode) next; String desc = cast.desc; // Non-array types are specified as internal names rather than // descriptions if (!desc.startsWith("[")) { desc = "L" + desc + ";"; } state.push(new CastArg(desc, state.pop())); }; handlers[INSTANCEOF] = (state, next) -> { TypeInsnNode insn = (TypeInsnNode) next; Instruction val = state.pop(); String type = insn.desc; if (!type.startsWith("[")) { type = "L" + insn.desc + ";"; } state.push(new InstanceOfArg(val, type)); }; handlers[MONITORENTER] = noop; handlers[MONITOREXIT] = noop; handlers[MULTIANEWARRAY] = null; // TODO MULTIANEWARRAY handlers[IFNULL] = null; // handled in the post process handlers[IFNONNULL] = null; // -- } /** * A handler for binary operators. */ static class OperatorHandler implements OpHandler { private final BiFunction<Instruction, Instruction, OperatorArg> ctor; public OperatorHandler(BiFunction<Instruction, Instruction, OperatorArg> ctor) { this.ctor = ctor; } @Override public void handle(OpcodeDecompiler state, AbstractInsnNode next) { Instruction right = state.pop(); Instruction left = state.pop(); OperatorArg arg = this.ctor.apply(left, right); state.push(arg); } } /** * A handler for CHECKCAST opcodes. */ static class CastHandler implements OpHandler { private final String type; public CastHandler(String t) { this.type = t; } @Override public void handle(OpcodeDecompiler state, AbstractInsnNode next) { Instruction val = state.pop(); CastArg arg = new CastArg(this.type, val); state.push(arg); } } }