Instruction.java
/*
* Copyright (c) 2001-2024, Jean Tessier
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Jean Tessier nor the names of his contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jeantessier.classreader.impl;
import com.jeantessier.classreader.BootstrapMethodFinder;
import com.jeantessier.classreader.LocalVariableFinder;
import com.jeantessier.classreader.Visitor;
import org.apache.logging.log4j.*;
import java.util.*;
public class Instruction implements com.jeantessier.classreader.Instruction {
private static final int NB_OPCODES = 0x100;
private static final String[] opcode = new String[NB_OPCODES];
private static final int[] length = new int[NB_OPCODES];
static {
opcode[0x00] = "nop";
length[0x00] = 1;
opcode[0x01] = "aconst_null";
length[0x01] = 1;
opcode[0x02] = "iconst_m1";
length[0x02] = 1;
opcode[0x03] = "iconst_0";
length[0x03] = 1;
opcode[0x04] = "iconst_1";
length[0x04] = 1;
opcode[0x05] = "iconst_2";
length[0x05] = 1;
opcode[0x06] = "iconst_3";
length[0x06] = 1;
opcode[0x07] = "iconst_4";
length[0x07] = 1;
opcode[0x08] = "iconst_5";
length[0x08] = 1;
opcode[0x09] = "lconst_0";
length[0x09] = 1;
opcode[0x0a] = "lconst_1";
length[0x0a] = 1;
opcode[0x0b] = "fconst_0";
length[0x0b] = 1;
opcode[0x0c] = "fconst_1";
length[0x0c] = 1;
opcode[0x0d] = "fconst_2";
length[0x0d] = 1;
opcode[0x0e] = "dconst_0";
length[0x0e] = 1;
opcode[0x0f] = "dconst_1";
length[0x0f] = 1;
opcode[0x10] = "bipush";
length[0x10] = 2;
opcode[0x11] = "sipush";
length[0x11] = 3;
opcode[0x12] = "ldc";
length[0x12] = 2;
opcode[0x13] = "ldc_w";
length[0x13] = 3;
opcode[0x14] = "ldc2_w";
length[0x14] = 3;
opcode[0x15] = "iload";
length[0x15] = 2;
opcode[0x16] = "lload";
length[0x16] = 2;
opcode[0x17] = "fload";
length[0x17] = 2;
opcode[0x18] = "dload";
length[0x18] = 2;
opcode[0x19] = "aload";
length[0x19] = 2;
opcode[0x1a] = "iload_0";
length[0x1a] = 1;
opcode[0x1b] = "iload_1";
length[0x1b] = 1;
opcode[0x1c] = "iload_2";
length[0x1c] = 1;
opcode[0x1d] = "iload_3";
length[0x1d] = 1;
opcode[0x1e] = "lload_0";
length[0x1e] = 1;
opcode[0x1f] = "lload_1";
length[0x1f] = 1;
opcode[0x20] = "lload_2";
length[0x20] = 1;
opcode[0x21] = "lload_3";
length[0x21] = 1;
opcode[0x22] = "fload_0";
length[0x22] = 1;
opcode[0x23] = "fload_1";
length[0x23] = 1;
opcode[0x24] = "fload_2";
length[0x24] = 1;
opcode[0x25] = "fload_3";
length[0x25] = 1;
opcode[0x26] = "dload_0";
length[0x26] = 1;
opcode[0x27] = "dload_1";
length[0x27] = 1;
opcode[0x28] = "dload_2";
length[0x28] = 1;
opcode[0x29] = "dload_3";
length[0x29] = 1;
opcode[0x2a] = "aload_0";
length[0x2a] = 1;
opcode[0x2b] = "aload_1";
length[0x2b] = 1;
opcode[0x2c] = "aload_2";
length[0x2c] = 1;
opcode[0x2d] = "aload_3";
length[0x2d] = 1;
opcode[0x2e] = "iaload";
length[0x2e] = 1;
opcode[0x2f] = "laload";
length[0x2f] = 1;
opcode[0x30] = "faload";
length[0x30] = 1;
opcode[0x31] = "daload";
length[0x31] = 1;
opcode[0x32] = "aaload";
length[0x32] = 1;
opcode[0x33] = "baload";
length[0x33] = 1;
opcode[0x34] = "caload";
length[0x34] = 1;
opcode[0x35] = "saload";
length[0x35] = 1;
opcode[0x36] = "istore";
length[0x36] = 2;
opcode[0x37] = "lstore";
length[0x37] = 2;
opcode[0x38] = "fstore";
length[0x38] = 2;
opcode[0x39] = "dstore";
length[0x39] = 2;
opcode[0x3a] = "astore";
length[0x3a] = 2;
opcode[0x3b] = "istore_0";
length[0x3b] = 1;
opcode[0x3c] = "istore_1";
length[0x3c] = 1;
opcode[0x3d] = "istore_2";
length[0x3d] = 1;
opcode[0x3e] = "istore_3";
length[0x3e] = 1;
opcode[0x3f] = "lstore_0";
length[0x3f] = 1;
opcode[0x40] = "lstore_1";
length[0x40] = 1;
opcode[0x41] = "lstore_2";
length[0x41] = 1;
opcode[0x42] = "lstore_3";
length[0x42] = 1;
opcode[0x43] = "fstore_0";
length[0x43] = 1;
opcode[0x44] = "fstore_1";
length[0x44] = 1;
opcode[0x45] = "fstore_2";
length[0x45] = 1;
opcode[0x46] = "fstore_3";
length[0x46] = 1;
opcode[0x47] = "dstore_0";
length[0x47] = 1;
opcode[0x48] = "dstore_1";
length[0x48] = 1;
opcode[0x49] = "dstore_2";
length[0x49] = 1;
opcode[0x4a] = "dstore_3";
length[0x4a] = 1;
opcode[0x4b] = "astore_0";
length[0x4b] = 1;
opcode[0x4c] = "astore_1";
length[0x4c] = 1;
opcode[0x4d] = "astore_2";
length[0x4d] = 1;
opcode[0x4e] = "astore_3";
length[0x4e] = 1;
opcode[0x4f] = "iastore";
length[0x4f] = 1;
opcode[0x50] = "lastore";
length[0x50] = 1;
opcode[0x51] = "fastore";
length[0x51] = 1;
opcode[0x52] = "dastore";
length[0x52] = 1;
opcode[0x53] = "aastore";
length[0x53] = 1;
opcode[0x54] = "bastore";
length[0x54] = 1;
opcode[0x55] = "castore";
length[0x55] = 1;
opcode[0x56] = "sastore";
length[0x56] = 1;
opcode[0x57] = "pop";
length[0x57] = 1;
opcode[0x58] = "pop2";
length[0x58] = 1;
opcode[0x59] = "dup";
length[0x59] = 1;
opcode[0x5a] = "dup_x1";
length[0x5a] = 1;
opcode[0x5b] = "dup_x2";
length[0x5b] = 1;
opcode[0x5c] = "dup2";
length[0x5c] = 1;
opcode[0x5d] = "dup2_x1";
length[0x5d] = 1;
opcode[0x5e] = "dup2_x2";
length[0x5e] = 1;
opcode[0x5f] = "swap";
length[0x5f] = 1;
opcode[0x60] = "iadd";
length[0x60] = 1;
opcode[0x61] = "ladd";
length[0x61] = 1;
opcode[0x62] = "fadd";
length[0x62] = 1;
opcode[0x63] = "dadd";
length[0x63] = 1;
opcode[0x64] = "isub";
length[0x64] = 1;
opcode[0x65] = "lsub";
length[0x65] = 1;
opcode[0x66] = "fsub";
length[0x66] = 1;
opcode[0x67] = "dsub";
length[0x67] = 1;
opcode[0x68] = "imul";
length[0x68] = 1;
opcode[0x69] = "lmul";
length[0x69] = 1;
opcode[0x6a] = "fmul";
length[0x6a] = 1;
opcode[0x6b] = "dmul";
length[0x6b] = 1;
opcode[0x6c] = "idiv";
length[0x6c] = 1;
opcode[0x6d] = "ldiv";
length[0x6d] = 1;
opcode[0x6e] = "fdiv";
length[0x6e] = 1;
opcode[0x6f] = "ddiv";
length[0x6f] = 1;
opcode[0x70] = "irem";
length[0x70] = 1;
opcode[0x71] = "lrem";
length[0x71] = 1;
opcode[0x72] = "frem";
length[0x72] = 1;
opcode[0x73] = "drem";
length[0x73] = 1;
opcode[0x74] = "ineg";
length[0x74] = 1;
opcode[0x75] = "lneg";
length[0x75] = 1;
opcode[0x76] = "fneg";
length[0x76] = 1;
opcode[0x77] = "dneg";
length[0x77] = 1;
opcode[0x78] = "ishl";
length[0x78] = 1;
opcode[0x79] = "lshl";
length[0x79] = 1;
opcode[0x7a] = "ishr";
length[0x7a] = 1;
opcode[0x7b] = "lshr";
length[0x7b] = 1;
opcode[0x7c] = "iushr";
length[0x7c] = 1;
opcode[0x7d] = "lushr";
length[0x7d] = 1;
opcode[0x7e] = "iand";
length[0x7e] = 1;
opcode[0x7f] = "land";
length[0x7f] = 1;
opcode[0x80] = "ior";
length[0x80] = 1;
opcode[0x81] = "lor";
length[0x81] = 1;
opcode[0x82] = "ixor";
length[0x82] = 1;
opcode[0x83] = "lxor";
length[0x83] = 1;
opcode[0x84] = "iinc";
length[0x84] = 3;
opcode[0x85] = "i2l";
length[0x85] = 1;
opcode[0x86] = "i2f";
length[0x86] = 1;
opcode[0x87] = "i2d";
length[0x87] = 1;
opcode[0x88] = "l2i";
length[0x88] = 1;
opcode[0x89] = "l2f";
length[0x89] = 1;
opcode[0x8a] = "l2d";
length[0x8a] = 1;
opcode[0x8b] = "f2i";
length[0x8b] = 1;
opcode[0x8c] = "f2l";
length[0x8c] = 1;
opcode[0x8d] = "f2d";
length[0x8d] = 1;
opcode[0x8e] = "d2i";
length[0x8e] = 1;
opcode[0x8f] = "d2l";
length[0x8f] = 1;
opcode[0x90] = "d2f";
length[0x90] = 1;
opcode[0x91] = "i2b";
length[0x91] = 1;
opcode[0x92] = "i2c";
length[0x92] = 1;
opcode[0x93] = "i2s";
length[0x93] = 1;
opcode[0x94] = "lcmp";
length[0x94] = 1;
opcode[0x95] = "fcmpl";
length[0x95] = 1;
opcode[0x96] = "fcmpg";
length[0x96] = 1;
opcode[0x97] = "dcmpl";
length[0x97] = 1;
opcode[0x98] = "dcmpg";
length[0x98] = 1;
opcode[0x99] = "ifeq";
length[0x99] = 3;
opcode[0x9a] = "ifne";
length[0x9a] = 3;
opcode[0x9b] = "iflt";
length[0x9b] = 3;
opcode[0x9c] = "ifge";
length[0x9c] = 3;
opcode[0x9d] = "ifgt";
length[0x9d] = 3;
opcode[0x9e] = "ifle";
length[0x9e] = 3;
opcode[0x9f] = "if_icmpeq";
length[0x9f] = 3;
opcode[0xa0] = "if_icmpne";
length[0xa0] = 3;
opcode[0xa1] = "if_icmplt";
length[0xa1] = 3;
opcode[0xa2] = "if_icmpge";
length[0xa2] = 3;
opcode[0xa3] = "if_icmpgt";
length[0xa3] = 3;
opcode[0xa4] = "if_icmple";
length[0xa4] = 3;
opcode[0xa5] = "if_acmpeq";
length[0xa5] = 3;
opcode[0xa6] = "if_acmpne";
length[0xa6] = 3;
opcode[0xa7] = "goto";
length[0xa7] = 3;
opcode[0xa8] = "jsr";
length[0xa8] = 3;
opcode[0xa9] = "ret";
length[0xa9] = 2;
opcode[0xaa] = "tableswitch";
length[0xaa] = -1;
opcode[0xab] = "lookupswitch";
length[0xab] = -1;
opcode[0xac] = "ireturn";
length[0xac] = 1;
opcode[0xad] = "lreturn";
length[0xad] = 1;
opcode[0xae] = "freturn";
length[0xae] = 1;
opcode[0xaf] = "dreturn";
length[0xaf] = 1;
opcode[0xb0] = "areturn";
length[0xb0] = 1;
opcode[0xb1] = "return";
length[0xb1] = 1;
opcode[0xb2] = "getstatic";
length[0xb2] = 3;
opcode[0xb3] = "putstatic";
length[0xb3] = 3;
opcode[0xb4] = "getfield";
length[0xb4] = 3;
opcode[0xb5] = "putfield";
length[0xb5] = 3;
opcode[0xb6] = "invokevirtual";
length[0xb6] = 3;
opcode[0xb7] = "invokespecial";
length[0xb7] = 3;
opcode[0xb8] = "invokestatic";
length[0xb8] = 3;
opcode[0xb9] = "invokeinterface";
length[0xb9] = 5;
opcode[0xba] = "invokedynamic";
length[0xba] = 5;
opcode[0xbb] = "new";
length[0xbb] = 3;
opcode[0xbc] = "newarray";
length[0xbc] = 2;
opcode[0xbd] = "anewarray";
length[0xbd] = 3;
opcode[0xbe] = "arraylength";
length[0xbe] = 1;
opcode[0xbf] = "athrow";
length[0xbf] = 1;
opcode[0xc0] = "checkcast";
length[0xc0] = 3;
opcode[0xc1] = "instanceof";
length[0xc1] = 3;
opcode[0xc2] = "monitorenter";
length[0xc2] = 1;
opcode[0xc3] = "monitorexit";
length[0xc3] = 1;
opcode[0xc4] = "wide";
length[0xc4] = -1;
opcode[0xc5] = "multianewarray";
length[0xc5] = 4;
opcode[0xc6] = "ifnull";
length[0xc6] = 3;
opcode[0xc7] = "ifnonnull";
length[0xc7] = 3;
opcode[0xc8] = "goto_w";
length[0xc8] = 5;
opcode[0xc9] = "jsr_w";
length[0xc9] = 5;
opcode[0xca] = "breakpoint";
length[0xca] = 1;
opcode[0xcb] = "xxxundefinedxxx";
length[0xcb] = 1;
opcode[0xcc] = "xxxundefinedxxx";
length[0xcc] = 1;
opcode[0xcd] = "xxxundefinedxxx";
length[0xcd] = 1;
opcode[0xce] = "xxxundefinedxxx";
length[0xce] = 1;
opcode[0xcf] = "xxxundefinedxxx";
length[0xcf] = 1;
opcode[0xd0] = "xxxundefinedxxx";
length[0xd0] = 1;
opcode[0xd1] = "xxxundefinedxxx";
length[0xd1] = 1;
opcode[0xd2] = "xxxundefinedxxx";
length[0xd2] = 1;
opcode[0xd3] = "xxxundefinedxxx";
length[0xd3] = 1;
opcode[0xd4] = "xxxundefinedxxx";
length[0xd4] = 1;
opcode[0xd5] = "xxxundefinedxxx";
length[0xd5] = 1;
opcode[0xd6] = "xxxundefinedxxx";
length[0xd6] = 1;
opcode[0xd7] = "xxxundefinedxxx";
length[0xd7] = 1;
opcode[0xd8] = "xxxundefinedxxx";
length[0xd8] = 1;
opcode[0xd9] = "xxxundefinedxxx";
length[0xd9] = 1;
opcode[0xda] = "xxxundefinedxxx";
length[0xda] = 1;
opcode[0xdb] = "xxxundefinedxxx";
length[0xdb] = 1;
opcode[0xdc] = "xxxundefinedxxx";
length[0xdc] = 1;
opcode[0xdd] = "xxxundefinedxxx";
length[0xdd] = 1;
opcode[0xde] = "xxxundefinedxxx";
length[0xde] = 1;
opcode[0xdf] = "xxxundefinedxxx";
length[0xdf] = 1;
opcode[0xe0] = "xxxundefinedxxx";
length[0xe0] = 1;
opcode[0xe1] = "xxxundefinedxxx";
length[0xe1] = 1;
opcode[0xe2] = "xxxundefinedxxx";
length[0xe2] = 1;
opcode[0xe3] = "xxxundefinedxxx";
length[0xe3] = 1;
opcode[0xe4] = "xxxundefinedxxx";
length[0xe4] = 1;
opcode[0xe5] = "xxxundefinedxxx";
length[0xe5] = 1;
opcode[0xe6] = "xxxundefinedxxx";
length[0xe6] = 1;
opcode[0xe7] = "xxxundefinedxxx";
length[0xe7] = 1;
opcode[0xe8] = "xxxundefinedxxx";
length[0xe8] = 1;
opcode[0xe9] = "xxxundefinedxxx";
length[0xe9] = 1;
opcode[0xea] = "xxxundefinedxxx";
length[0xea] = 1;
opcode[0xeb] = "xxxundefinedxxx";
length[0xeb] = 1;
opcode[0xec] = "xxxundefinedxxx";
length[0xec] = 1;
opcode[0xed] = "xxxundefinedxxx";
length[0xed] = 1;
opcode[0xee] = "xxxundefinedxxx";
length[0xee] = 1;
opcode[0xef] = "xxxundefinedxxx";
length[0xef] = 1;
opcode[0xf0] = "xxxundefinedxxx";
length[0xf0] = 1;
opcode[0xf1] = "xxxundefinedxxx";
length[0xf1] = 1;
opcode[0xf2] = "xxxundefinedxxx";
length[0xf2] = 1;
opcode[0xf3] = "xxxundefinedxxx";
length[0xf3] = 1;
opcode[0xf4] = "xxxundefinedxxx";
length[0xf4] = 1;
opcode[0xf5] = "xxxundefinedxxx";
length[0xf5] = 1;
opcode[0xf6] = "xxxundefinedxxx";
length[0xf6] = 1;
opcode[0xf7] = "xxxundefinedxxx";
length[0xf7] = 1;
opcode[0xf8] = "xxxundefinedxxx";
length[0xf8] = 1;
opcode[0xf9] = "xxxundefinedxxx";
length[0xf9] = 1;
opcode[0xfa] = "xxxundefinedxxx";
length[0xfa] = 1;
opcode[0xfb] = "xxxundefinedxxx";
length[0xfb] = 1;
opcode[0xfc] = "xxxundefinedxxx";
length[0xfc] = 1;
opcode[0xfd] = "xxxundefinedxxx";
length[0xfd] = 1;
opcode[0xfe] = "impdep1";
length[0xfe] = 1;
opcode[0xff] = "impdep2";
length[0xff] = 1;
}
private final Code_attribute code;
private final byte[] bytecode;
private final int start;
public Instruction(Code_attribute code, byte[] bytecode, int start) {
this.code = code;
this.bytecode = bytecode;
this.start = start;
}
public byte[] getBytecode() {
return bytecode;
}
public int getStart() {
return start;
}
public int getOpcode() {
return getByte(0);
}
public static String getMnemonic(int instruction) {
return opcode[instruction];
}
public String getMnemonic() {
String result = getMnemonic(getOpcode());
if (getOpcode() == 0xc4 /* wide */) {
result += " " + getMnemonic(getByte(1));
}
return result;
}
public int getLength() {
return switch (getOpcode()) {
case 0xaa: // tableswitch
yield
1 + // opcode
getPadding() + // padding
12 + // (default, low, high) signed 32-bits values
(getHigh() - getLow() + 1) * 4; // (high - low + 1) signed 32-bits values
case 0xab: // lookupswitch
yield
1 + // opcode
getPadding() + // padding
8 + // (default, npairs) signed 32-bits values
(getNPairs() * 8); // npairs * (match, offset) signed 32-bits value
case 0xc4: // wide
yield getByte(1) == 0x84 /* iinc */ ? 6 : 4;
default:
yield length[getOpcode()];
};
}
public int getIndex() {
return switch (getOpcode()) {
case 0x13: // ldc_w
case 0x14: // ldc2_w
case 0xb2: // getstatic
case 0xb3: // putstatic
case 0xb4: // getfield
case 0xb5: // putfield
case 0xb6: // invokevirtual
case 0xb7: // invokespecial
case 0xb8: // invokestatic
case 0xb9: // invokeinterface
case 0xba: // invokedynamic
case 0xbb: // new
case 0xbd: // anewarray
case 0xc0: // checkcast
case 0xc1: // instanceof
case 0xc5: // multianewarray
yield getShort(1);
case 0x1a: // iload_0
case 0x1e: // lload_0
case 0x22: // fload_0
case 0x26: // dload_0
case 0x2a: // aload_0
case 0x3b: // istore_0
case 0x3f: // lstore_0
case 0x43: // fstore_0
case 0x47: // dstore_0
case 0x4b: // astore_0
yield 0;
case 0x1b: // iload_1
case 0x1f: // lload_1
case 0x23: // fload_1
case 0x27: // dload_1
case 0x2b: // aload_1
case 0x3c: // istore_1
case 0x40: // lstore_1
case 0x44: // fstore_1
case 0x48: // dstore_1
case 0x4c: // astore_1
yield 1;
case 0x1c: // iload_2
case 0x20: // lload_2
case 0x24: // fload_2
case 0x28: // dload_2
case 0x2c: // aload_2
case 0x3d: // istore_2
case 0x41: // lstore_2
case 0x45: // fstore_2
case 0x49: // dstore_2
case 0x4d: // astore_2
yield 2;
case 0x1d: // iload_3
case 0x21: // lload_3
case 0x25: // fload_3
case 0x29: // dload_3
case 0x2d: // aload_3
case 0x3e: // istore_3
case 0x42: // lstore_3
case 0x46: // fstore_3
case 0x4a: // dstore_3
case 0x4e: // astore_3
yield 3;
case 0x12: // ldc
case 0x15: // iload
case 0x16: // llload
case 0x17: // fload
case 0x18: // dload
case 0x19: // aload
case 0x36: // istore
case 0x37: // lstore
case 0x38: // fstore
case 0x39: // dstore
case 0x3a: // astore
case 0x84: // iinc
case 0xa9: // ret
yield getByte(1);
case 0xc4: // wide
yield getShort(2);
default:
yield -1;
};
}
public int getOffset() {
return switch (getOpcode()) {
case 0x99: // ifeq
case 0x9a: // ifne
case 0x9b: // iflt
case 0x9c: // ifge
case 0x9d: // ifgt
case 0x9e: // ifle
case 0x9f: // if_icmpeq
case 0xa0: // if_icmpne
case 0xa1: // if_icmplt
case 0xa2: // if_icmpge
case 0xa3: // if_icmpgt
case 0xa4: // if_icmple
case 0xa5: // if_acmpeq
case 0xa6: // if_acmpne
case 0xa7: // goto
case 0xa8: // jsr
case 0xc6: // ifnull
case 0xc7: // ifnonnull
yield getSignedShort(1);
case 0xc8: // goto_w
case 0xc9: // jsr_w
yield getInt(1);
default:
yield 0;
};
}
public int getValue() {
return switch (getOpcode()) {
case 0x02: // iconst_m1
yield -1;
case 0x03: // iconst_0
case 0x09: // lconst_0
case 0x0b: // fconst_0
case 0x0e: // dconst_0
yield 0;
case 0x04: // iconst_1
case 0x0a: // lconst_1
case 0x0c: // fconst_1
case 0x0f: // dconst_1
yield 1;
case 0x05: // iconst_2
case 0x0d: // fconst_2
yield 2;
case 0x06: // iconst_3
yield 3;
case 0x07: // iconst_4
yield 4;
case 0x08: // iconst_5
yield 5;
case 0x10: // bipush
yield getSignedByte(1);
case 0x11: // sipush
yield getSignedShort(1);
case 0x84: // iinc
yield getSignedByte(2);
case 0xc4: // wide
yield getByte(1) == 0x84 /* iinc */ ? getSignedShort(4) : 0;
default:
yield 0;
};
}
public int getPadding() {
return 3 - (start % 4);
}
public int getDefault() {
return getInt(getPadding() + 1);
}
public int getLow() {
return getInt(getPadding() + 5);
}
public int getHigh() {
return getInt(getPadding() + 9);
}
public int getNPairs() {
return getInt(getPadding() + 5);
}
private byte getSignedByte(int offset) {
return getBytecode()[getStart() + offset];
}
public int getByte(int offset) {
return getSignedByte(offset) & 0xff;
}
private int getSignedShort(int offset) {
return (getSignedByte(offset+0) << 8) | (getByte(offset+1));
}
private int getShort(int offset) {
return (getByte(offset+0) << 8) | (getByte(offset+1));
}
public int getInt(int offset) {
return (getByte(offset+0) << 24) | (getByte(offset+1) << 16) | (getByte(offset+2) << 8) | (getByte(offset+3));
}
public com.jeantessier.classreader.ConstantPoolEntry getIndexedConstantPoolEntry() {
return switch (getOpcode()) {
case 0x12: // ldc
case 0x13: // ldc_w
case 0x14: // ldc2_w
case 0xb2: // getstatic
case 0xb3: // putstatic
case 0xb4: // getfield
case 0xb5: // putfield
case 0xb6: // invokevirtual
case 0xb7: // invokespecial
case 0xb8: // invokestatic
case 0xb9: // invokeinterface
case 0xba: // invokedynamic
case 0xbb: // new
case 0xbd: // anewarray
case 0xc0: // checkcast
case 0xc1: // instanceof
case 0xc5: // multianewarray
yield code.getConstantPool().get(getIndex());
default:
yield null;
};
}
public Collection<? extends ConstantPoolEntry> getDynamicConstantPoolEntries() {
return switch (getOpcode()) {
case 0xba: // invokedynamic
LogManager.getLogger(getClass()).debug("getDynamicConstantPoolEntries()");
if (getIndexedConstantPoolEntry() instanceof Dynamic_info entry) {
BootstrapMethodFinder finder = new BootstrapMethodFinder(entry.getBootstrapMethodAttrIndex());
code.getConstantPool().getClassfile().accept(finder);
yield finder.getBootstrapMethod().getArguments().stream()
.filter(argument -> argument instanceof MethodHandle_info)
.map(methodHandle -> ((MethodHandle_info) methodHandle).getReference())
.toList();
} else if (getIndexedConstantPoolEntry() instanceof InvokeDynamic_info entry) {
BootstrapMethodFinder finder = new BootstrapMethodFinder(entry.getBootstrapMethodAttrIndex());
code.getConstantPool().getClassfile().accept(finder);
yield finder.getBootstrapMethod().getArguments().stream()
.filter(argument -> argument instanceof MethodHandle_info)
.map(methodHandle -> ((MethodHandle_info) methodHandle).getReference())
.toList();
} else {
yield Collections.emptyList();
}
default:
yield Collections.emptyList();
};
// TODO: Replace with type pattern matching in switch expression in Java 21
// return switch (getIndexedConstantPoolEntry()) {
// case Dynamic_info entry -> {
// BootstrapMethodFinder finder = new BootstrapMethodFinder(entry.getBootstrapMethodAttrIndex());
// code.getConstantPool().getClassfile().accept(finder);
// yield finder.getBootstrapMethod().getArguments().stream()
// .filter(argument -> argument instanceof MethodHandle_info)
// .map(methodHandle -> ((MethodHandle_info) methodHandle).getReference());
// }
// case InvokeDynamic_info entry -> {
// BootstrapMethodFinder finder = new BootstrapMethodFinder(entry.getBootstrapMethodAttrIndex());
// code.getConstantPool().getClassfile().accept(finder);
// yield finder.getBootstrapMethod().getArguments().stream()
// .filter(argument -> argument instanceof MethodHandle_info)
// .map(methodHandle -> ((MethodHandle_info) methodHandle).getReference());
// }
// default -> Stream.empty();
// };
}
public com.jeantessier.classreader.LocalVariable getIndexedLocalVariable() {
return switch (getOpcode()) {
case 0x1a: // iload_0
case 0x1e: // lload_0
case 0x22: // fload_0
case 0x26: // dload_0
case 0x2a: // aload_0
case 0x1b: // iload_1
case 0x1f: // lload_1
case 0x23: // fload_1
case 0x27: // dload_1
case 0x2b: // aload_1
case 0x1c: // iload_2
case 0x20: // lload_2
case 0x24: // fload_2
case 0x28: // dload_2
case 0x2c: // aload_2
case 0x1d: // iload_3
case 0x21: // lload_3
case 0x25: // fload_3
case 0x29: // dload_3
case 0x2d: // aload_3
case 0x15: // iload
case 0x16: // llload
case 0x17: // fload
case 0x18: // dload
case 0x19: // aload
case 0x84: // iinc
case 0xa9: // ret
yield locateLocalVariable(getStart());
case 0x3b: // istore_0
case 0x3f: // lstore_0
case 0x43: // fstore_0
case 0x47: // dstore_0
case 0x4b: // astore_0
case 0x3c: // istore_1
case 0x40: // lstore_1
case 0x44: // fstore_1
case 0x48: // dstore_1
case 0x4c: // astore_1
case 0x3d: // istore_2
case 0x41: // lstore_2
case 0x45: // fstore_2
case 0x49: // dstore_2
case 0x4d: // astore_2
case 0x3e: // istore_3
case 0x42: // lstore_3
case 0x46: // fstore_3
case 0x4a: // dstore_3
case 0x4e: // astore_3
case 0x36: // istore
case 0x37: // lstore
case 0x38: // fstore
case 0x39: // dstore
case 0x3a: // astore
yield locateLocalVariable(getStart() + getLength());
case 0xc4: // wide
if (getByte(1) >= 0x36 && getByte(1) <= 0x3a) {
yield locateLocalVariable(getStart() + getLength());
} else {
yield locateLocalVariable(getStart());
}
default:
yield null;
};
}
private com.jeantessier.classreader.LocalVariable locateLocalVariable(int pc) {
LocalVariableFinder finder = new LocalVariableFinder(getIndex(), pc);
code.accept(finder);
return finder.getLocalVariable();
}
public int hashCode() {
int result = getOpcode();
if (getIndexedConstantPoolEntry() != null) {
result ^= getIndexedConstantPoolEntry().hashCode();
} else {
for (int i=1; i<getLength(); i++) {
result ^= bytecode[start+i];
}
}
return result;
}
public boolean equals(Object object) {
boolean result;
if (this == object) {
result = true;
} else if (object == null || getClass() != object.getClass()) {
result = false;
} else {
Instruction other = (Instruction) object;
result = getOpcode() == other.getOpcode();
ConstantPoolEntry thisEntry = (ConstantPoolEntry) ((code != null) ? getIndexedConstantPoolEntry() : null);
ConstantPoolEntry otherEntry = (ConstantPoolEntry) ((other.code != null) ? other.getIndexedConstantPoolEntry() : null);
if (result && thisEntry != null && otherEntry != null) {
result = thisEntry.equals(otherEntry);
} else {
for (int i=1; result && i<getLength(); i++) {
result = bytecode[start+i] == other.bytecode[other.start+i];
}
}
}
return result;
}
public String toString() {
return getMnemonic();
}
public void accept(Visitor visitor) {
visitor.visitInstruction(this);
}
}