diff --git a/plugins/kotlin/src/main/java/org/vineflower/kotlin/KotlinWriter.java b/plugins/kotlin/src/main/java/org/vineflower/kotlin/KotlinWriter.java index f683d79202..1019f482f6 100644 --- a/plugins/kotlin/src/main/java/org/vineflower/kotlin/KotlinWriter.java +++ b/plugins/kotlin/src/main/java/org/vineflower/kotlin/KotlinWriter.java @@ -5,8 +5,7 @@ import net.fabricmc.fernflower.api.IFabricJavadocProvider; import org.jetbrains.java.decompiler.api.plugin.StatementWriter; 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.FullInstructionSequence; import org.jetbrains.java.decompiler.main.*; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; import org.jetbrains.java.decompiler.main.collectors.ImportCollector; @@ -1222,21 +1221,19 @@ public static void collectErrorLines(Throwable error, List lines) { private static void collectBytecode(MethodWrapper wrapper, List lines) throws IOException { ClassNode classNode = DecompilerContext.getContextProperty(DecompilerContext.CURRENT_CLASS_NODE); StructMethod method = wrapper.methodStruct; - InstructionSequence instructions = method.getInstructionSequence(); + FullInstructionSequence instructions = method.getInstructionSequence(); if (instructions == null) { method.expandData(classNode.classStruct); instructions = method.getInstructionSequence(); } - int lastOffset = instructions.getOffset(instructions.length() - 1); + int lastOffset = instructions.getLast().startOffset; int digits = 8 - Integer.numberOfLeadingZeros(lastOffset) / 4; ConstantPool pool = classNode.classStruct.getPool(); StructBootstrapMethodsAttribute bootstrap = classNode.classStruct.getAttribute(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS); - for (int idx = 0; idx < instructions.length(); idx++) { - int offset = instructions.getOffset(idx); - Instruction instr = instructions.getInstr(idx); + for (var instr : instructions) { StringBuilder sb = new StringBuilder(); - String offHex = Integer.toHexString(offset); + String offHex = Integer.toHexString(instr.startOffset); sb.append("0".repeat(Math.max(0, digits - offHex.length()))); sb.append(offHex).append(": "); if (instr.wide) { @@ -1261,7 +1258,7 @@ private static void collectBytecode(MethodWrapper wrapper, List lines) t } case CodeConstants.GROUP_JUMP -> { sb.append(' '); - int dest = offset + instr.operand(0); + int dest = instr.startOffset + instr.operand(0); String destHex = Integer.toHexString(dest); sb.append("0".repeat(Math.max(0, digits - destHex.length()))); sb.append(destHex); diff --git a/src/org/jetbrains/java/decompiler/code/ExceptionHandler.java b/src/org/jetbrains/java/decompiler/code/ExceptionHandler.java index 0d20726006..a4b5ab1b07 100644 --- a/src/org/jetbrains/java/decompiler/code/ExceptionHandler.java +++ b/src/org/jetbrains/java/decompiler/code/ExceptionHandler.java @@ -3,21 +3,15 @@ import org.jetbrains.java.decompiler.main.DecompilerContext; -public class ExceptionHandler { - public int from = 0; - public int to = 0; - public int handler = 0; - - public int from_instr = 0; - public int to_instr = 0; - public int handler_instr = 0; - - public String exceptionClass = null; - +public record ExceptionHandler( + int from, + int to, + int handler, + String exceptionClass +) { public String toString() { - String new_line_separator = DecompilerContext.getNewLineSeparator(); - return "from: " + from + " to: " + to + " handler: " + handler + new_line_separator + - "from_instr: " + from_instr + " to_instr: " + to_instr + " handler_instr: " + handler_instr + new_line_separator + - "exceptionClass: " + exceptionClass + new_line_separator; + String newLineSeparator = DecompilerContext.getNewLineSeparator(); + return "from instr: " + this.from + " to instr: " + this.to + " handler instr: " + handler + + newLineSeparator + "exception class: " + this.exceptionClass + newLineSeparator; } } \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/code/FullInstructionSequence.java b/src/org/jetbrains/java/decompiler/code/FullInstructionSequence.java index dacf5ffbc8..c27c410d72 100644 --- a/src/org/jetbrains/java/decompiler/code/FullInstructionSequence.java +++ b/src/org/jetbrains/java/decompiler/code/FullInstructionSequence.java @@ -1,25 +1,61 @@ // Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.code; -import org.jetbrains.java.decompiler.util.collections.VBStyleCollection; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.util.TextUtil; +import java.util.*; -public class FullInstructionSequence extends InstructionSequence { - // ***************************************************************************** - // constructors - // ***************************************************************************** +public record FullInstructionSequence( + List instructions, + Map offsetToIndex, + ExceptionTable exceptionTable +) implements Iterable { - public FullInstructionSequence(VBStyleCollection collinstr, ExceptionTable extable) { - super(collinstr); - this.exceptionTable = extable; + public Instruction getInstr(int index) { + return this.instructions.get(index); + } + + public int length() { + return this.instructions.size(); + } + + public boolean isEmpty() { + return this.instructions.isEmpty(); + } + + public int getIndexByRelOffset(Instruction instruction, int offset) { + int absoluteOffset = instruction.startOffset + offset; + return offsetToIndex.getOrDefault(absoluteOffset, -1); + } + + @Override + public Iterator iterator() { + return this.instructions.iterator(); + } + + public String toString() { + return toString(0); + } + + public String toString(int indent) { + String new_line_separator = DecompilerContext.getNewLineSeparator(); - // translate raw exception handlers to instr - for (ExceptionHandler handler : extable.getHandlers()) { - handler.from_instr = this.getPointerByAbsOffset(handler.from); - int toIndex = this.getPointerByAbsOffset(handler.to); - handler.to_instr = toIndex == -1 ? this.collinstr.size() : toIndex; - handler.handler_instr = this.getPointerByAbsOffset(handler.handler); + StringBuilder buf = new StringBuilder(); + + for (var instr : this.instructions) { + buf.append(TextUtil.getIndentString(indent)); + buf.append(instr.startOffset); + buf.append(": "); + buf.append(instr); + buf.append(new_line_separator); } + + return buf.toString(); + } + + public Instruction getLast() { + return this.instructions.get(this.instructions.size() - 1); } } diff --git a/src/org/jetbrains/java/decompiler/code/Instruction.java b/src/org/jetbrains/java/decompiler/code/Instruction.java index 32ac062017..5b265898b4 100644 --- a/src/org/jetbrains/java/decompiler/code/Instruction.java +++ b/src/org/jetbrains/java/decompiler/code/Instruction.java @@ -5,18 +5,26 @@ import static org.jetbrains.java.decompiler.code.CodeConstants.*; public class Instruction implements CodeConstants { - public static Instruction create(int opcode, boolean wide, int group, BytecodeVersion bytecodeVersion, int[] operands, int length) { + public static Instruction create( + int opcode, + boolean wide, + int group, + BytecodeVersion bytecodeVersion, + int[] operands, + int startOffset, + int length + ) { if (opcode >= opc_ifeq && opcode <= opc_if_acmpne || opcode == opc_ifnull || opcode == opc_ifnonnull || opcode == opc_jsr || opcode == opc_jsr_w || opcode == opc_goto || opcode == opc_goto_w) { - return new JumpInstruction(opcode, group, wide, bytecodeVersion, operands, length); + return new JumpInstruction(opcode, group, wide, bytecodeVersion, operands, startOffset, length); } else if (opcode == opc_tableswitch || opcode == opc_lookupswitch) { - return new SwitchInstruction(opcode, group, wide, bytecodeVersion, operands, length); + return new SwitchInstruction(opcode, group, wide, bytecodeVersion, operands, startOffset, length); } else { - return new Instruction(opcode, group, wide, bytecodeVersion, operands, length); + return new Instruction(opcode, group, wide, bytecodeVersion, operands, startOffset, length); } } @@ -32,20 +40,30 @@ public static boolean equals(Instruction i1, Instruction i2) { public final int group; public final boolean wide; public final BytecodeVersion bytecodeVersion; + public final int startOffset; public final int length; protected final int[] operands; - public Instruction(int opcode, int group, boolean wide, BytecodeVersion bytecodeVersion, int[] operands, int length) { + public Instruction( + int opcode, + int group, + boolean wide, + BytecodeVersion bytecodeVersion, + int[] operands, + int startOffset, + int length + ) { this.opcode = opcode; this.group = group; this.wide = wide; this.bytecodeVersion = bytecodeVersion; this.operands = operands; + this.startOffset = startOffset; this.length = length; } - public void initInstruction(InstructionSequence seq) { } + public void initInstruction(FullInstructionSequence seq) { } public int operandsCount() { return operands == null ? 0 : operands.length; @@ -85,6 +103,6 @@ public String toString() { @Override @SuppressWarnings("MethodDoesntCallSuperMethod") public Instruction clone() { - return create(opcode, wide, group, bytecodeVersion, operands == null ? null : operands.clone(), length); + return create(opcode, wide, group, bytecodeVersion, operands == null ? null : operands.clone(), startOffset, length); } } diff --git a/src/org/jetbrains/java/decompiler/code/InstructionSequence.java b/src/org/jetbrains/java/decompiler/code/InstructionSequence.java index 84434e54bc..4f1bc33718 100644 --- a/src/org/jetbrains/java/decompiler/code/InstructionSequence.java +++ b/src/org/jetbrains/java/decompiler/code/InstructionSequence.java @@ -3,116 +3,72 @@ import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.util.TextUtil; -import org.jetbrains.java.decompiler.util.collections.VBStyleCollection; +import java.util.ArrayList; +import java.util.Collection; import java.util.Iterator; +import java.util.List; -public abstract class InstructionSequence implements Iterable { +public class InstructionSequence implements Iterable { + private final List instructions; - // ***************************************************************************** - // private fields - // ***************************************************************************** - - protected final VBStyleCollection collinstr; - - protected int pointer = 0; - - protected ExceptionTable exceptionTable = ExceptionTable.EMPTY; - - protected InstructionSequence() { - this(new VBStyleCollection<>()); + public InstructionSequence() { + this(new ArrayList<>()); } - protected InstructionSequence(VBStyleCollection collinstr) { - this.collinstr = collinstr; + public InstructionSequence(Collection instructions) { + this.instructions = new ArrayList<>(instructions); } // ***************************************************************************** // public methods // ***************************************************************************** - // to nbe overwritten @Override public InstructionSequence clone() { - return null; + return new InstructionSequence(this.instructions); // Constructor takes a copy } public void clear() { - collinstr.clear(); - pointer = 0; - exceptionTable = ExceptionTable.EMPTY; + this.instructions.clear(); } - public void addInstruction(Instruction inst, int offset) { - collinstr.addWithKey(inst, offset); + public void addInstruction(Instruction inst) { + this.instructions.add(inst); } - public void addInstruction(int index, Instruction inst, int offset) { - collinstr.addWithKeyAndIndex(index, inst, offset); + public void addInstruction(int index, Instruction inst) { + this.instructions.add(index, inst); } public void addSequence(InstructionSequence seq) { - for (int i = 0; i < seq.length(); i++) { - addInstruction(seq.getInstr(i), -1); // TODO: any sensible value possible? - } + this.instructions.addAll(seq.instructions); } public void removeInstruction(int index) { - collinstr.remove(index); - } - - public void removeInstruction(Instruction inst) { - // VBStyle remove(Object) is not implemented - collinstr.removeIf(i -> i == inst); + this.instructions.remove(index); } public void removeLast() { - if (!collinstr.isEmpty()) { - collinstr.remove(collinstr.size() - 1); + if (!this.instructions.isEmpty()) { + this.instructions.remove(this.instructions.size() - 1); } } public Instruction getInstr(int index) { - return collinstr.get(index); + return this.instructions.get(index); } public Instruction getLastInstr() { - return collinstr.getLast(); - } - - public int getOffset(int index) { - return collinstr.getKey(index); - } - - public int getPointerByAbsOffset(int offset) { - if (collinstr.containsKey(offset)) { - return collinstr.getIndexByKey(offset); - } - else { - return -1; - } - } - - public int getPointerByRelOffset(int offset) { - int absoffset = collinstr.getKey(pointer) + offset; - if (collinstr.containsKey(absoffset)) { - return collinstr.getIndexByKey(absoffset); - } - else { - return -1; - } + return this.instructions.get(this.instructions.size() - 1); } public int length() { - return collinstr.size(); + return this.instructions.size(); } public boolean isEmpty() { - return collinstr.isEmpty(); - } - - public void addToPointer(int diff) { - this.pointer += diff; + return this.instructions.isEmpty(); } public String toString() { @@ -125,11 +81,11 @@ public String toString(int indent) { StringBuilder buf = new StringBuilder(); - for (int i = 0; i < collinstr.size(); i++) { - buf.append(TextUtil.getIndentString(indent)); - buf.append((int) collinstr.getKey(i)); + for (var instr : this.instructions) { + buf.append(TextUtil.getIndentString(indent)); + buf.append(instr.startOffset); buf.append(": "); - buf.append(collinstr.get(i).toString()); + buf.append(instr); buf.append(new_line_separator); } @@ -140,20 +96,8 @@ public String toString(int indent) { // getter and setter methods // ***************************************************************************** - public int getPointer() { - return pointer; - } - - public void setPointer(int pointer) { - this.pointer = pointer; - } - - public ExceptionTable getExceptionTable() { - return exceptionTable; - } - @Override public Iterator iterator() { - return this.collinstr.iterator(); + return this.instructions.iterator(); } } \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/code/JumpInstruction.java b/src/org/jetbrains/java/decompiler/code/JumpInstruction.java index bf05974e6e..d10544d1f6 100644 --- a/src/org/jetbrains/java/decompiler/code/JumpInstruction.java +++ b/src/org/jetbrains/java/decompiler/code/JumpInstruction.java @@ -4,18 +4,26 @@ public class JumpInstruction extends Instruction { public int destination; - public JumpInstruction(int opcode, int group, boolean wide, BytecodeVersion bytecodeVersion, int[] operands, int length) { - super(opcode, group, wide, bytecodeVersion, operands, length); + public JumpInstruction( + int opcode, + int group, + boolean wide, + BytecodeVersion bytecodeVersion, + int[] operands, + int startOffset, + int length + ) { + super(opcode, group, wide, bytecodeVersion, operands, startOffset, length); } @Override - public void initInstruction(InstructionSequence seq) { - destination = seq.getPointerByRelOffset(this.operand(0)); + public void initInstruction(FullInstructionSequence seq) { + destination = seq.getIndexByRelOffset(this, this.operand(0)); } @Override public JumpInstruction clone() { - JumpInstruction copy = (JumpInstruction)super.clone(); + JumpInstruction copy = (JumpInstruction) super.clone(); copy.destination = destination; return copy; } diff --git a/src/org/jetbrains/java/decompiler/code/SimpleInstructionSequence.java b/src/org/jetbrains/java/decompiler/code/SimpleInstructionSequence.java deleted file mode 100644 index a87449f76c..0000000000 --- a/src/org/jetbrains/java/decompiler/code/SimpleInstructionSequence.java +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -package org.jetbrains.java.decompiler.code; - -import org.jetbrains.java.decompiler.util.collections.VBStyleCollection; - -public class SimpleInstructionSequence extends InstructionSequence { - - public SimpleInstructionSequence() { - } - - public SimpleInstructionSequence(VBStyleCollection collinstr) { - super(collinstr); - } - - @Override - public SimpleInstructionSequence clone() { - SimpleInstructionSequence newseq = new SimpleInstructionSequence(collinstr.clone()); - newseq.setPointer(this.getPointer()); - - return newseq; - } -} diff --git a/src/org/jetbrains/java/decompiler/code/SwitchInstruction.java b/src/org/jetbrains/java/decompiler/code/SwitchInstruction.java index 4bfe26f2de..48b19252cf 100644 --- a/src/org/jetbrains/java/decompiler/code/SwitchInstruction.java +++ b/src/org/jetbrains/java/decompiler/code/SwitchInstruction.java @@ -6,21 +6,28 @@ public class SwitchInstruction extends Instruction { private int[] values; private int defaultDestination; - public SwitchInstruction(int opcode, int group, boolean wide, BytecodeVersion bytecodeVersion, int[] operands, int length) { - super(opcode, group, wide, bytecodeVersion, operands, length); + public SwitchInstruction( + int opcode, + int group, + boolean wide, + BytecodeVersion bytecodeVersion, + int[] operands, + int startOffset, + int length + ) { + super(opcode, group, wide, bytecodeVersion, operands, startOffset, length); } @Override - public void initInstruction(InstructionSequence seq) { - defaultDestination = seq.getPointerByRelOffset(operands[0]); + public void initInstruction(FullInstructionSequence seq) { + defaultDestination = seq.getIndexByRelOffset(this, operands[0]); int prefix = opcode == CodeConstants.opc_tableswitch ? 3 : 2; int len = operands.length - prefix; int low = 0; if (opcode == CodeConstants.opc_lookupswitch) { len /= 2; - } - else { + } else { low = operands[1]; } @@ -30,11 +37,10 @@ public void initInstruction(InstructionSequence seq) { if (opcode == CodeConstants.opc_lookupswitch) { values[i] = operands[prefix + k]; k++; - } - else { + } else { values[i] = low + k; } - destinations[i] = seq.getPointerByRelOffset(operands[prefix + k]); + destinations[i] = seq.getIndexByRelOffset(this, operands[prefix + k]); } } @@ -52,7 +58,7 @@ public int getDefaultDestination() { @Override public SwitchInstruction clone() { - SwitchInstruction copy = (SwitchInstruction)super.clone(); + SwitchInstruction copy = (SwitchInstruction) super.clone(); copy.defaultDestination = defaultDestination; copy.destinations = destinations.clone(); copy.values = values.clone(); diff --git a/src/org/jetbrains/java/decompiler/code/cfg/BasicBlock.java b/src/org/jetbrains/java/decompiler/code/cfg/BasicBlock.java index b4c598bbd5..e3f390336c 100644 --- a/src/org/jetbrains/java/decompiler/code/cfg/BasicBlock.java +++ b/src/org/jetbrains/java/decompiler/code/cfg/BasicBlock.java @@ -3,7 +3,6 @@ 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.main.DecompilerContext; import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraphNode; @@ -24,7 +23,7 @@ public class BasicBlock implements IGraphNode { // private fields // ***************************************************************************** - private InstructionSequence seq = new SimpleInstructionSequence(); + private InstructionSequence seq = new InstructionSequence(); private final List preds = new ArrayList<>(); private final List succs = new ArrayList<>(); diff --git a/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java b/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java index 8ab03bb6cc..fc45895205 100644 --- a/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java +++ b/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java @@ -36,7 +36,7 @@ public class ControlFlowGraph implements CodeConstants { private Map subroutines; private final Set finallyExits = new HashSet<>(); - private final InstructionSequence sequence; + private final FullInstructionSequence sequence; public Set commentLines = null; public boolean addErrorComment = false; @@ -44,7 +44,7 @@ public class ControlFlowGraph implements CodeConstants { // constructors // ***************************************************************************** - public ControlFlowGraph(InstructionSequence seq) { + public ControlFlowGraph(FullInstructionSequence seq) { this.sequence = seq; buildBlocks(seq); @@ -194,7 +194,7 @@ public ExceptionRangeCFG getExceptionRange(BasicBlock handler, BasicBlock block) // private methods // ***************************************************************************** - private void buildBlocks(InstructionSequence instrseq) { + private void buildBlocks(FullInstructionSequence instrseq) { short[] states = findStartInstructions(instrseq); @@ -221,17 +221,17 @@ private void buildBlocks(InstructionSequence instrseq) { *
  • instructions after a return, throw or after a jump
  • * */ - private static short[] findStartInstructions(InstructionSequence seq) { + private static short[] findStartInstructions(FullInstructionSequence seq) { int len = seq.length(); short[] inststates = new short[len]; // exception blocks - for (ExceptionHandler handler : seq.getExceptionTable().getHandlers()) { - inststates[handler.from_instr] = 1; - inststates[handler.handler_instr] = 1; + for (ExceptionHandler handler : seq.exceptionTable().getHandlers()) { + inststates[handler.from()] = 1; + inststates[handler.handler()] = 1; - if (handler.to_instr < len) { - inststates[handler.to_instr] = 1; + if (handler.to() < len) { + inststates[handler.to()] = 1; } } @@ -275,7 +275,8 @@ private static short[] findStartInstructions(InstructionSequence seq) { */ private Map createBasicBlocks( short[] startblock, - InstructionSequence instrseq) { + FullInstructionSequence instrSeq + ) { Map mapInstrBlocks = new HashMap<>(); this.blocks = new VBStyleCollection<>(); @@ -285,7 +286,6 @@ private Map createBasicBlocks( int len = startblock.length; short counter = 0; - int blockoffset = 0; BasicBlock currentBlock = null; for (int i = 0; i < len; i++) { @@ -298,8 +298,6 @@ private Map createBasicBlocks( // index: $i, key: $i+1, value BasicBlock(id = $i + 1) this.blocks.addWithKey(currentBlock, currentBlock.id); - - blockoffset = instrseq.getOffset(i); } startblock[i] = counter; @@ -307,8 +305,9 @@ private Map createBasicBlocks( // can't throw npe cause startblock[0] == 1 is always true assert currseq != null && lstOffs != null; - currseq.addInstruction(instrseq.getInstr(i), instrseq.getOffset(i) - blockoffset); - lstOffs.add(instrseq.getOffset(i)); + Instruction instr = instrSeq.getInstr(i); + currseq.addInstruction(instr); + lstOffs.add(instr.startOffset); } this.first = this.blocks.get(0); @@ -383,7 +382,7 @@ private void connectBlocks(Map mapInstrBlocks) { } } - private void setExceptionEdges(InstructionSequence instrseq, Map instrBlocks) { + private void setExceptionEdges(FullInstructionSequence instrseq, Map instrBlocks) { exceptions = new ArrayList<>(); @@ -392,18 +391,18 @@ private void setExceptionEdges(InstructionSequence instrseq, Map getFinallyExits() { return finallyExits; } - public InstructionSequence getSequence() { + public FullInstructionSequence getSequence() { return sequence; } diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index 869a20f653..fc8fb12dd7 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -5,8 +5,7 @@ import net.fabricmc.fernflower.api.IFabricJavadocProvider; import org.jetbrains.java.decompiler.api.plugin.StatementWriter; 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.FullInstructionSequence; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; import org.jetbrains.java.decompiler.main.collectors.ImportCollector; import org.jetbrains.java.decompiler.main.decompiler.CancelationManager; @@ -1404,21 +1403,19 @@ public static void collectErrorLines(Throwable error, List lines) { private static void collectBytecode(MethodWrapper wrapper, List lines) throws IOException { ClassNode classNode = (ClassNode)DecompilerContext.getContextProperty(DecompilerContext.CURRENT_CLASS_NODE); StructMethod method = wrapper.methodStruct; - InstructionSequence instructions = method.getInstructionSequence(); + FullInstructionSequence instructions = method.getInstructionSequence(); if (instructions == null) { method.expandData(classNode.classStruct); instructions = method.getInstructionSequence(); } - int lastOffset = instructions.getOffset(instructions.length() - 1); + int lastOffset = instructions.getLast().startOffset; int digits = 8 - Integer.numberOfLeadingZeros(lastOffset) / 4; ConstantPool pool = classNode.classStruct.getPool(); StructBootstrapMethodsAttribute bootstrap = classNode.classStruct.getAttribute(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS); - for (int idx = 0; idx < instructions.length(); idx++) { - int offset = instructions.getOffset(idx); - Instruction instr = instructions.getInstr(idx); + for (var instr : instructions) { StringBuilder sb = new StringBuilder(); - String offHex = Integer.toHexString(offset); + String offHex = Integer.toHexString(instr.startOffset); for (int i = offHex.length(); i < digits; i++) sb.append('0'); sb.append(offHex).append(": "); if (instr.wide) { @@ -1445,7 +1442,7 @@ private static void collectBytecode(MethodWrapper wrapper, List lines) t } case CodeConstants.GROUP_JUMP: { sb.append(' '); - int dest = offset + instr.operand(0); + int dest = instr.startOffset + instr.operand(0); String destHex = Integer.toHexString(dest); for (int i = destHex.length(); i < digits; i++) sb.append('0'); sb.append(destHex); diff --git a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java index 1fc3c0a1f1..364acef0ee 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java @@ -4,8 +4,7 @@ import org.jetbrains.java.decompiler.api.plugin.StatementWriter; import org.jetbrains.java.decompiler.api.plugin.LanguageSpec; 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.FullInstructionSequence; import org.jetbrains.java.decompiler.main.collectors.BytecodeSourceMapper; import org.jetbrains.java.decompiler.main.collectors.ImportCollector; import org.jetbrains.java.decompiler.main.decompiler.CancelationManager; @@ -364,11 +363,9 @@ else if (cl.superClass == null) { // neither interface nor super class defined try { mt.expandData(enclosingCl); - InstructionSequence seq = mt.getInstructionSequence(); + FullInstructionSequence seq = mt.getInstructionSequence(); if (seq != null) { - int len = seq.length(); - for (int i = 0; i < len; i++) { - Instruction instr = seq.getInstr(i); + for (var instr : seq) { switch (instr.opcode) { case opc_checkcast: case opc_instanceof: diff --git a/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java b/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java index 4d4caa9a62..6cc476d907 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java @@ -2,8 +2,7 @@ package org.jetbrains.java.decompiler.main.rels; 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.FullInstructionSequence; import org.jetbrains.java.decompiler.main.ClassesProcessor; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; import org.jetbrains.java.decompiler.main.DecompilerContext; @@ -65,13 +64,9 @@ public void processClass(ClassNode node) throws IOException { for (StructMethod mt : cl.getMethods()) { mt.expandData(cl); - InstructionSequence seq = mt.getInstructionSequence(); - if (seq != null && seq.length() > 0) { - int len = seq.length(); - - for (int i = 0; i < len; ++i) { - Instruction instr = seq.getInstr(i); - + FullInstructionSequence seq = mt.getInstructionSequence(); + if (seq != null) { + for (var instr : seq) { if (instr.opcode == CodeConstants.opc_invokedynamic) { LinkConstant invoke_dynamic = cl.getPool().getLinkConstant(instr.operand(0)); diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessor.java b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessor.java index c7ef4f4033..00911c7662 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessor.java @@ -5,7 +5,7 @@ import org.jetbrains.java.decompiler.api.plugin.LanguageSpec; import org.jetbrains.java.decompiler.api.plugin.pass.PassContext; import org.jetbrains.java.decompiler.code.CodeConstants; -import org.jetbrains.java.decompiler.code.InstructionSequence; +import org.jetbrains.java.decompiler.code.FullInstructionSequence; import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; @@ -95,7 +95,7 @@ public static RootStatement codeToJava(StructClass cl, StructMethod mt, MethodDe PluginContext pluginContext = PluginContext.getCurrentContext(); mt.expandData(cl); - InstructionSequence seq = mt.getInstructionSequence(); + FullInstructionSequence seq = mt.getInstructionSequence(); ControlFlowGraph graph = new ControlFlowGraph(seq); debugCurrentCFG.set(graph); DotExporter.toDotFile(graph, mt, "cfgConstructed", true); diff --git a/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java b/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java index 8cd34890ea..7b53a9762c 100644 --- a/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java @@ -7,8 +7,6 @@ import org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; -import org.jetbrains.java.decompiler.struct.StructMethod; -import org.jetbrains.java.decompiler.util.DotExporter; import java.util.*; @@ -414,7 +412,7 @@ public static void extendSynchronizedRangeToMonitorexit(ControlFlowGraph graph) // FIXME: what is this splitting doing, and why does it cause the loop to never finish? // if(succ_monitorexit_index < succSeq.length() - 1) { // split block // -// SimpleInstructionSequence seq = new SimpleInstructionSequence(); +// InstructionSequence seq = new InstructionSequence(); // for(int counter = 0; counter < succ_monitorexit_index; counter++) { // seq.addInstruction(succSeq.getInstr(0), -1); // succSeq.removeInstruction(0); @@ -449,7 +447,7 @@ public static void extendSynchronizedRangeToMonitorexit(ControlFlowGraph graph) // copy instructions (handler successor block) InstructionSequence handlerSeq = handlerBlock.getSeq(); for(int counter = 0; counter < handler_monitorexit_index; counter++) { - handlerSeq.addInstruction(succHandlerSeq.getInstr(0), -1); + handlerSeq.addInstruction(succHandlerSeq.getInstr(0)); succHandlerSeq.removeInstruction(0); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java index 0e41f4efd5..917e8bab72 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java @@ -18,7 +18,6 @@ import org.jetbrains.java.decompiler.modules.decompiler.flow.DirectGraph; import org.jetbrains.java.decompiler.modules.decompiler.flow.DirectNode; import org.jetbrains.java.decompiler.modules.decompiler.flow.FlattenStatementsHelper; -import org.jetbrains.java.decompiler.modules.decompiler.sforms.SFormsConstructor; import org.jetbrains.java.decompiler.modules.decompiler.sforms.SSAConstructorSparseEx; import org.jetbrains.java.decompiler.modules.decompiler.sforms.SSAUConstructorSparseEx; import org.jetbrains.java.decompiler.modules.decompiler.sforms.SimpleSSAReassign; @@ -351,9 +350,9 @@ private static void insertSemaphore(ControlFlowGraph graph, // break out if (dest != graph.getLast() && !setCopy.contains(dest)) { // disable semaphore - SimpleInstructionSequence seq = new SimpleInstructionSequence(); - seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}, 1), -1); - seq.addInstruction(Instruction.create(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}, store_length), -1); + InstructionSequence seq = new InstructionSequence(); + seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}, -1, 1)); + seq.addInstruction(Instruction.create(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}, -1, store_length)); // build a separate block BasicBlock newblock = new BasicBlock(++graph.last_id); @@ -381,9 +380,9 @@ private static void insertSemaphore(ControlFlowGraph graph, } // enable semaphore at the statement entrance - SimpleInstructionSequence seq = new SimpleInstructionSequence(); - seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{1}, 1), -1); - seq.addInstruction(Instruction.create(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}, store_length), -1); + InstructionSequence seq = new InstructionSequence(); + seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{1}, -1, 1)); + seq.addInstruction(Instruction.create(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}, -1, store_length)); BasicBlock newhead = new BasicBlock(++graph.last_id); newhead.setSeq(seq); @@ -391,9 +390,9 @@ private static void insertSemaphore(ControlFlowGraph graph, insertBlockBefore(graph, head, newhead); // initialize semaphor with false - seq = new SimpleInstructionSequence(); - seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}, 1), -1); - seq.addInstruction(Instruction.create(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}, store_length), -1); + seq = new InstructionSequence(); + seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}, -1, 1)); + seq.addInstruction(Instruction.create(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}, -1, store_length)); BasicBlock newheadinit = new BasicBlock(++graph.last_id); newheadinit.setSeq(seq); @@ -833,10 +832,10 @@ private boolean compareBasicBlocksEx(ControlFlowGraph graph, } if (seqPattern.length() < seqSample.length()) { // split in two blocks - SimpleInstructionSequence seq = new SimpleInstructionSequence(); + InstructionSequence seq = new InstructionSequence(); LinkedList oldOffsets = new LinkedList<>(); for (int i = seqSample.length() - 1; i >= seqPattern.length(); i--) { - seq.addInstruction(0, seqSample.getInstr(i), -1); + seq.addInstruction(0, seqSample.getInstr(i)); oldOffsets.addFirst(sample.getOldOffset(i)); seqSample.removeInstruction(i); if (i < instrOldOffsetsSample.size()) { @@ -1150,7 +1149,7 @@ private static void inlineReturnVar(ControlFlowGraph graph, BasicBlock handler) // remove store exit.getSeq().removeLast(); // add return - exit.getSeq().addInstruction(nextSeq.getInstr(1), -1); + exit.getSeq().addInstruction(nextSeq.getInstr(1)); // Clear next exception range, mergeBasicBlocks will take care of it nextSeq.clear(); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java b/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java index e0f2494e5d..4174098cf6 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java @@ -91,8 +91,8 @@ public static void restorePopRanges(ControlFlowGraph graph) { // split the handler if (seq.length() > 1) { newblock = new BasicBlock(++graph.last_id); - InstructionSequence newseq = new SimpleInstructionSequence(); - newseq.addInstruction(firstinstr.clone(), -1); + InstructionSequence newseq = new InstructionSequence(); + newseq.addInstruction(firstinstr.clone()); newblock.setSeq(newseq); graph.getBlocks().addWithKey(newblock, newblock.id); @@ -411,9 +411,9 @@ public static void insertDummyExceptionHandlerBlocks(ControlFlowGraph graph, Byt for (ExceptionRangeCFG range : ranges) { // add some dummy instructions to prevent optimizing away the empty block - SimpleInstructionSequence seq = new SimpleInstructionSequence(); - seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}, 1), -1); - seq.addInstruction(Instruction.create(CodeConstants.opc_pop, false, CodeConstants.GROUP_GENERAL, bytecode_version, null, 1), -1); + InstructionSequence seq = new InstructionSequence(); + seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}, -1, 1)); + seq.addInstruction(Instruction.create(CodeConstants.opc_pop, false, CodeConstants.GROUP_GENERAL, bytecode_version, null, -1, 1)); BasicBlock dummyBlock = new BasicBlock(++graph.last_id); dummyBlock.setSeq(seq); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java index 393af9fa14..9b45e011f3 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java @@ -4,7 +4,6 @@ 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.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; @@ -97,9 +96,9 @@ 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); + InstructionSequence seq = new InstructionSequence(); + for (var instr : block.getSeq()) { + seq.addInstruction(instr.clone()); } newblock.setSeq(seq); diff --git a/src/org/jetbrains/java/decompiler/struct/StructMethod.java b/src/org/jetbrains/java/decompiler/struct/StructMethod.java index 53a2d01a99..399f8341fc 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructMethod.java +++ b/src/org/jetbrains/java/decompiler/struct/StructMethod.java @@ -14,10 +14,10 @@ import org.jetbrains.java.decompiler.struct.gen.generics.GenericMain; import org.jetbrains.java.decompiler.struct.gen.generics.GenericMethodDescriptor; import org.jetbrains.java.decompiler.util.DataInputFullStream; -import org.jetbrains.java.decompiler.util.collections.VBStyleCollection; import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -67,7 +67,7 @@ public static StructMethod create(DataInputFullStream in, ConstantPool pool, Str private final BytecodeVersion bytecodeVersion; private final int localVariables; private final byte[] codeAndExceptions; - private InstructionSequence seq = null; + private FullInstructionSequence seq = null; private boolean expanded = false; private final String classQualifiedName; private final GenericMethodDescriptor signature; @@ -111,8 +111,9 @@ public void releaseResources() { } @SuppressWarnings("AssignmentToForLoopParameter") - private InstructionSequence parseBytecode(DataInputFullStream in, ConstantPool pool) throws IOException { - VBStyleCollection instructions = new VBStyleCollection<>(); + private FullInstructionSequence parseBytecode(DataInputFullStream in, ConstantPool pool) throws IOException { + List instructions = new ArrayList<>(); + Map offsetToIndex = new HashMap<>(); int length = in.readInt(); for (int i = 0; i < length; ) { @@ -319,9 +320,10 @@ else if (opcode >= opc_invokevirtual && opcode <= opc_invokestatic) { i++; - Instruction instr = Instruction.create(opcode, wide, group, bytecodeVersion, ops, i - offset); + Instruction instr = Instruction.create(opcode, wide, group, bytecodeVersion, ops, offset, i - offset); - instructions.addWithKey(instr, offset); + offsetToIndex.put(offset, instructions.size()); + instructions.add(instr); } // initialize exception table @@ -329,31 +331,29 @@ else if (opcode >= opc_invokevirtual && opcode <= opc_invokestatic) { int exception_count = in.readUnsignedShort(); for (int i = 0; i < exception_count; i++) { - ExceptionHandler handler = new ExceptionHandler(); - handler.from = in.readUnsignedShort(); - handler.to = in.readUnsignedShort(); - handler.handler = in.readUnsignedShort(); - - int excclass = in.readUnsignedShort(); - if (excclass != 0) { - handler.exceptionClass = pool.getPrimitiveConstant(excclass).getString(); + int from = offsetToIndex.get(in.readUnsignedShort()); + // catch block can go to the end of the method + int to = offsetToIndex.getOrDefault(in.readUnsignedShort(), instructions.size()); + int handler = offsetToIndex.get(in.readUnsignedShort()); + + int excClass = in.readUnsignedShort(); + String exceptionClass; + if (excClass == 0) { + exceptionClass = null; + } else { + exceptionClass = pool.getPrimitiveConstant(excClass).getString(); } - lstHandlers.add(handler); + lstHandlers.add(new ExceptionHandler(from, to, handler, exceptionClass)); } - InstructionSequence seq = new FullInstructionSequence(instructions, new ExceptionTable(lstHandlers)); + FullInstructionSequence seq = new FullInstructionSequence( + instructions, + offsetToIndex, + new ExceptionTable(lstHandlers)); - // initialize instructions - int i = seq.length() - 1; - seq.setPointer(i); - - while (i >= 0) { - Instruction instr = seq.getInstr(i--); - if (instr.group != GROUP_GENERAL) { - instr.initInstruction(seq); - } - seq.addToPointer(-1); + for (var instr : seq) { + instr.initInstruction(seq); } return seq; @@ -383,7 +383,7 @@ public int getLocalVariables() { return localVariables; } - public InstructionSequence getInstructionSequence() { + public FullInstructionSequence getInstructionSequence() { return seq; }