dropped InstructionParser in favor of MethodBytecodeUtil (and ASM)

This commit is contained in:
Egor.Ushakov
2016-07-06 18:29:10 +03:00
parent a08ceff560
commit a516994054
4 changed files with 140 additions and 861 deletions
@@ -1,585 +0,0 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.debugger.jdi;
/**
* As defined in ADM4 library
*/
class Bytecodes {
/**
* Pseudo access flag to distinguish between the synthetic attribute and
* the synthetic access flag.
*/
static final int ACC_SYNTHETIC_ATTRIBUTE = 0x40000;
/**
* The type of instructions without any argument.
*/
static final int NOARG_INSN = 0;
/**
* The type of instructions with an signed byte argument.
*/
static final int SBYTE_INSN = 1;
/**
* The type of instructions with an signed short argument.
*/
static final int SHORT_INSN = 2;
/**
* The type of instructions with a local variable index argument.
*/
static final int VAR_INSN = 3;
/**
* The type of instructions with an implicit local variable index argument.
*/
static final int IMPLVAR_INSN = 4;
/**
* The type of instructions with a type descriptor argument.
*/
static final int TYPE_INSN = 5;
/**
* The type of field and method invocations instructions.
*/
static final int FIELDORMETH_INSN = 6;
/**
* The type of the INVOKEINTERFACE/INVOKEDYNAMIC instruction.
*/
static final int ITFMETH_INSN = 7;
/**
* The type of the INVOKEDYNAMIC instruction.
*/
static final int INDYMETH_INSN = 8;
/**
* The type of instructions with a 2 bytes bytecode offset label.
*/
static final int LABEL_INSN = 9;
/**
* The type of instructions with a 4 bytes bytecode offset label.
*/
static final int LABELW_INSN = 10;
/**
* The type of the LDC instruction.
*/
static final int LDC_INSN = 11;
/**
* The type of the LDC_W and LDC2_W instructions.
*/
static final int LDCW_INSN = 12;
/**
* The type of the IINC instruction.
*/
static final int IINC_INSN = 13;
/**
* The type of the TABLESWITCH instruction.
*/
static final int TABL_INSN = 14;
/**
* The type of the LOOKUPSWITCH instruction.
*/
static final int LOOK_INSN = 15;
/**
* The type of the MULTIANEWARRAY instruction.
*/
static final int MANA_INSN = 16;
/**
* The type of the WIDE instruction.
*/
static final int WIDE_INSN = 17;
/**
* The instruction types of all JVM opcodes.
*/
static final byte[] TYPE;
/**
* The type of CONSTANT_Class constant pool items.
*/
static final int CLASS = 7;
/**
* The type of CONSTANT_Fieldref constant pool items.
*/
static final int FIELD = 9;
/**
* The type of CONSTANT_Methodref constant pool items.
*/
static final int METH = 10;
/**
* The type of CONSTANT_InterfaceMethodref constant pool items.
*/
static final int IMETH = 11;
/**
* The type of CONSTANT_String constant pool items.
*/
static final int STR = 8;
/**
* The type of CONSTANT_Integer constant pool items.
*/
static final int INT = 3;
/**
* The type of CONSTANT_Float constant pool items.
*/
static final int FLOAT = 4;
/**
* The type of CONSTANT_Long constant pool items.
*/
static final int LONG = 5;
/**
* The type of CONSTANT_Double constant pool items.
*/
static final int DOUBLE = 6;
/**
* The type of CONSTANT_NameAndType constant pool items.
*/
static final int NAME_TYPE = 12;
/**
* The type of CONSTANT_Utf8 constant pool items.
*/
static final int UTF8 = 1;
/**
* The type of CONSTANT_MethodType constant pool items.
*/
static final int MTYPE = 16;
/**
* The type of CONSTANT_MethodHandle constant pool items.
*/
static final int HANDLE = 15;
/**
* The type of CONSTANT_InvokeDynamic constant pool items.
*/
static final int INDY = 18;
/**
* The base value for all CONSTANT_MethodHandle constant pool items.
* Internally, ASM store the 9 variations of CONSTANT_MethodHandle into
* 9 different items.
*/
static final int HANDLE_BASE = 20;
/**
* Normal type Item stored in the ClassWriter {@link ClassWriter#typeTable},
* instead of the constant pool, in order to avoid clashes with normal
* constant pool items in the ClassWriter constant pool's hash table.
*/
static final int TYPE_NORMAL = 30;
/**
* Uninitialized type Item stored in the ClassWriter
* {@link ClassWriter#typeTable}, instead of the constant pool, in order to
* avoid clashes with normal constant pool items in the ClassWriter constant
* pool's hash table.
*/
static final int TYPE_UNINIT = 31;
/**
* Merged type Item stored in the ClassWriter {@link ClassWriter#typeTable},
* instead of the constant pool, in order to avoid clashes with normal
* constant pool items in the ClassWriter constant pool's hash table.
*/
static final int TYPE_MERGED = 32;
/**
* The type of BootstrapMethods items. These items are stored in a
* special class attribute named BootstrapMethods and
* not in the constant pool.
*/
static final int BSM = 33;
/*
Computes the instruction types of JVM opcodes.
*/
static {
int i;
byte[] b = new byte[220];
String s =
"AAAAAAAAAAAAAAAABCLMMDDDDDEEEEEEEEEEEEEEEEEEEEAAAAAAAADDDDDEE" +
"EEEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAANAAAAAAAAAAAAAAAAAAAAJJJJJJJJJJJJJJJJDOPAAAAAAGGGGG" +
"GGHIFBFAAFFAARQJJKKJLMMGGGGGGGGGGGGHGAFFQFFGGG";
for (i = 0; i < b.length; ++i) {
b[i] = (byte) (s.charAt(i) - 'A');
}
TYPE = b;
}
static final int NOP = 0; // visitInsn
static final int ACONST_NULL = 1; // -
static final int ICONST_M1 = 2; // -
static final int ICONST_0 = 3; // -
static final int ICONST_1 = 4; // -
static final int ICONST_2 = 5; // -
static final int ICONST_3 = 6; // -
static final int ICONST_4 = 7; // -
static final int ICONST_5 = 8; // -
static final int LCONST_0 = 9; // -
static final int LCONST_1 = 10; // -
static final int FCONST_0 = 11; // -
static final int FCONST_1 = 12; // -
static final int FCONST_2 = 13; // -
static final int DCONST_0 = 14; // -
static final int DCONST_1 = 15; // -
static final int BIPUSH = 16; // visitIntInsn
static final int SIPUSH = 17; // -
static final int LDC = 18; // visitLdcInsn
static final int LDC_W = 19; // -
static final int LDC2_W = 20; // -
static final int ILOAD = 21; // visitVarInsn
static final int LLOAD = 22; // -
static final int FLOAD = 23; // -
static final int DLOAD = 24; // -
static final int ALOAD = 25; // -
static final int ILOAD_0 = 26; // -
static final int ILOAD_1 = 27; // -
static final int ILOAD_2 = 28; // -
static final int ILOAD_3 = 29; // -
static final int LLOAD_0 = 30; // -
static final int LLOAD_1 = 31; // -
static final int LLOAD_2 = 32; // -
static final int LLOAD_3 = 33; // -
static final int FLOAD_0 = 34; // -
static final int FLOAD_1 = 35; // -
static final int FLOAD_2 = 36; // -
static final int FLOAD_3 = 37; // -
static final int DLOAD_0 = 38; // -
static final int DLOAD_1 = 39; // -
static final int DLOAD_2 = 40; // -
static final int DLOAD_3 = 41; // -
static final int ALOAD_0 = 42; // -
static final int ALOAD_1 = 43; // -
static final int ALOAD_2 = 44; // -
static final int ALOAD_3 = 45; // -
static final int IALOAD = 46; // visitInsn
static final int LALOAD = 47; // -
static final int FALOAD = 48; // -
static final int DALOAD = 49; // -
static final int AALOAD = 50; // -
static final int BALOAD = 51; // -
static final int CALOAD = 52; // -
static final int SALOAD = 53; // -
static final int ISTORE = 54; // visitVarInsn
static final int LSTORE = 55; // -
static final int FSTORE = 56; // -
static final int DSTORE = 57; // -
static final int ASTORE = 58; // -
static final int ISTORE_0 = 59; // -
static final int ISTORE_1 = 60; // -
static final int ISTORE_2 = 61; // -
static final int ISTORE_3 = 62; // -
static final int LSTORE_0 = 63; // -
static final int LSTORE_1 = 64; // -
static final int LSTORE_2 = 65; // -
static final int LSTORE_3 = 66; // -
static final int FSTORE_0 = 67; // -
static final int FSTORE_1 = 68; // -
static final int FSTORE_2 = 69; // -
static final int FSTORE_3 = 70; // -
static final int DSTORE_0 = 71; // -
static final int DSTORE_1 = 72; // -
static final int DSTORE_2 = 73; // -
static final int DSTORE_3 = 74; // -
static final int ASTORE_0 = 75; // -
static final int ASTORE_1 = 76; // -
static final int ASTORE_2 = 77; // -
static final int ASTORE_3 = 78; // -
static final int IASTORE = 79; // visitInsn
static final int LASTORE = 80; // -
static final int FASTORE = 81; // -
static final int DASTORE = 82; // -
static final int AASTORE = 83; // -
static final int BASTORE = 84; // -
static final int CASTORE = 85; // -
static final int SASTORE = 86; // -
static final int POP = 87; // -
static final int POP2 = 88; // -
static final int DUP = 89; // -
static final int DUP_X1 = 90; // -
static final int DUP_X2 = 91; // -
static final int DUP2 = 92; // -
static final int DUP2_X1 = 93; // -
static final int DUP2_X2 = 94; // -
static final int SWAP = 95; // -
static final int IADD = 96; // -
static final int LADD = 97; // -
static final int FADD = 98; // -
static final int DADD = 99; // -
static final int ISUB = 100; // -
static final int LSUB = 101; // -
static final int FSUB = 102; // -
static final int DSUB = 103; // -
static final int IMUL = 104; // -
static final int LMUL = 105; // -
static final int FMUL = 106; // -
static final int DMUL = 107; // -
static final int IDIV = 108; // -
static final int LDIV = 109; // -
static final int FDIV = 110; // -
static final int DDIV = 111; // -
static final int IREM = 112; // -
static final int LREM = 113; // -
static final int FREM = 114; // -
static final int DREM = 115; // -
static final int INEG = 116; // -
static final int LNEG = 117; // -
static final int FNEG = 118; // -
static final int DNEG = 119; // -
static final int ISHL = 120; // -
static final int LSHL = 121; // -
static final int ISHR = 122; // -
static final int LSHR = 123; // -
static final int IUSHR = 124; // -
static final int LUSHR = 125; // -
static final int IAND = 126; // -
static final int LAND = 127; // -
static final int IOR = 128; // -
static final int LOR = 129; // -
static final int IXOR = 130; // -
static final int LXOR = 131; // -
static final int IINC = 132; // visitIincInsn
static final int I2L = 133; // visitInsn
static final int I2F = 134; // -
static final int I2D = 135; // -
static final int L2I = 136; // -
static final int L2F = 137; // -
static final int L2D = 138; // -
static final int F2I = 139; // -
static final int F2L = 140; // -
static final int F2D = 141; // -
static final int D2I = 142; // -
static final int D2L = 143; // -
static final int D2F = 144; // -
static final int I2B = 145; // -
static final int I2C = 146; // -
static final int I2S = 147; // -
static final int LCMP = 148; // -
static final int FCMPL = 149; // -
static final int FCMPG = 150; // -
static final int DCMPL = 151; // -
static final int DCMPG = 152; // -
static final int IFEQ = 153; // visitJumpInsn
static final int IFNE = 154; // -
static final int IFLT = 155; // -
static final int IFGE = 156; // -
static final int IFGT = 157; // -
static final int IFLE = 158; // -
static final int IF_ICMPEQ = 159; // -
static final int IF_ICMPNE = 160; // -
static final int IF_ICMPLT = 161; // -
static final int IF_ICMPGE = 162; // -
static final int IF_ICMPGT = 163; // -
static final int IF_ICMPLE = 164; // -
static final int IF_ACMPEQ = 165; // -
static final int IF_ACMPNE = 166; // -
static final int GOTO = 167; // -
static final int JSR = 168; // -
static final int RET = 169; // visitVarInsn
static final int TABLESWITCH = 170; // visiTableSwitchInsn
static final int LOOKUPSWITCH = 171; // visitLookupSwitch
static final int IRETURN = 172; // visitInsn
static final int LRETURN = 173; // -
static final int FRETURN = 174; // -
static final int DRETURN = 175; // -
static final int ARETURN = 176; // -
static final int RETURN = 177; // -
static final int GETSTATIC = 178; // visitFieldInsn
static final int PUTSTATIC = 179; // -
static final int GETFIELD = 180; // -
static final int PUTFIELD = 181; // -
static final int INVOKEVIRTUAL = 182; // visitMethodInsn
static final int INVOKESPECIAL = 183; // -
static final int INVOKESTATIC = 184; // -
static final int INVOKEINTERFACE = 185; // -
static final int INVOKEDYNAMIC = 186; // visitInvokeDynamicInsn
static final int NEW = 187; // visitTypeInsn
static final int NEWARRAY = 188; // visitIntInsn
static final int ANEWARRAY = 189; // visitTypeInsn
static final int ARRAYLENGTH = 190; // visitInsn
static final int ATHROW = 191; // -
static final int CHECKCAST = 192; // visitTypeInsn
static final int INSTANCEOF = 193; // -
static final int MONITORENTER = 194; // visitInsn
static final int MONITOREXIT = 195; // -
static final int WIDE = 196; // NOT VISITED
static final int MULTIANEWARRAY = 197; // visitMultiANewArrayInsn
static final int IFNULL = 198; // visitJumpInsn
static final int IFNONNULL = 199; // -
static final int GOTO_W = 200; // -
static final int JSR_W = 201; // -
// JVM runtime-specific and reserved opcodes:
// From JVM specification:
// In addition to the opcodes of the instructions specified later in this chapter, which are used in class files (§4), three opcodes are reserved for internal use by a Java Virtual Machine implementation. If the instruction set of the Java Virtual Machine is extended in the future, these reserved opcodes are guaranteed not to be used.
// Two of the reserved opcodes, numbers 254 (0xfe) and 255 (0xff), have the mnemonics impdep1 and impdep2, respectively. These instructions are intended to provide "back doors" or traps to implementation-specific functionality implemented in software and hardware, respectively. The third reserved opcode, number 202 (0xca), has the mnemonic breakpoint and is intended to be used by debuggers to implement breakpoints.
// Although these opcodes have been reserved, they may be used only inside a Java Virtual Machine implementation. They cannot appear in valid class files. Tools such as debuggers or JIT code generators (§2.13) that might directly interact with Java Virtual Machine code that has been already loaded and executed may encounter these opcodes. Such tools should attempt to behave gracefully if they encounter any of these reserved instructions.
static final int BREAKPOINT = 202;
static final int LDC_QUICK = 203;
static final int LDC_W_QUICK = 204;
static final int LDC2_W_QUICK = 205;
static final int GETFIELD_QUICK = 206;
static final int PUTFIELD_QUICK = 207;
static final int GETFIELD2_QUICK = 208;
static final int PUTFIELD2_QUICK = 209;
static final int GETSTATIC_QUICK = 210;
static final int PUTSTATIC_QUICK = 211;
static final int GETSTATIC2_QUICK = 212;
static final int PUTSTATIC2_QUICK = 213;
static final int INVOKEVIRTUAL_QUICK = 214;
static final int INVOKENONVIRTUAL_QUICK = 215;
static final int INVOKESUPER_QUICK = 216;
static final int INVOKESTATIC_QUICK = 217;
static final int INVOKEINTERFACE_QUICK = 218;
static final int INVOKEVIRTUALOBJECT_QUICK = 219;
static final int NEW_QUICK = 221;
static final int ANEWARRAY_QUICK = 222;
static final int MULTIANEWARRAY_QUICK = 223;
static final int CHECKCAST_QUICK = 224;
static final int INSTANCEOF_QUICK = 225;
static final int INVOKEVIRTUAL_QUICK_W = 226;
static final int GETFIELD_QUICK_W = 227;
static final int PUTFIELD_QUICK_W = 228;
static final int IMPDEP1 = 254;
static final int IMPDEP2 = 255;
public static void main(String[] args) {
int[] b = new int[229];
//code to generate the above string
// SBYTE_INSN instructions
b[NEWARRAY] = SBYTE_INSN;
b[BIPUSH] = SBYTE_INSN;
// SHORT_INSN instructions
b[SIPUSH] = SHORT_INSN;
// (IMPL)VAR_INSN instructions
b[RET] = VAR_INSN;
for (int i = ILOAD; i <= ALOAD; ++i) {
b[i] = VAR_INSN;
}
for (int i = ISTORE; i <= ASTORE; ++i) {
b[i] = VAR_INSN;
}
for (int i = 26; i <= 45; ++i) { // ILOAD_0 to ALOAD_3
b[i] = IMPLVAR_INSN;
}
for (int i = 59; i <= 78; ++i) { // ISTORE_0 to ASTORE_3
b[i] = IMPLVAR_INSN;
}
// TYPE_INSN instructions
b[NEW] = TYPE_INSN;
b[ANEWARRAY] = TYPE_INSN;
b[CHECKCAST] = TYPE_INSN;
b[INSTANCEOF] = TYPE_INSN;
// (Set)FIELDORMETH_INSN instructions
for (int i = GETSTATIC; i <= INVOKESTATIC; ++i) {
b[i] = FIELDORMETH_INSN;
}
b[INVOKEINTERFACE] = ITFMETH_INSN;
b[INVOKEDYNAMIC] = INDYMETH_INSN;
// LABEL(W)_INSN instructions
for (int i = IFEQ; i <= JSR; ++i) {
b[i] = LABEL_INSN;
}
b[IFNULL] = LABEL_INSN;
b[IFNONNULL] = LABEL_INSN;
b[GOTO_W] = LABELW_INSN; // GOTO_W
b[JSR_W] = LABELW_INSN; // JSR_W
b[BREAKPOINT] = LABEL_INSN; // todo: is this correct?
// LDC(_W) instructions
b[LDC] = LDC_INSN;
b[LDC_W] = LDCW_INSN; // LDC_W
b[LDC2_W] = LDCW_INSN; // LDC2_W
// special instructions
b[IINC] = IINC_INSN;
b[TABLESWITCH] = TABL_INSN;
b[LOOKUPSWITCH] = LOOK_INSN;
b[MULTIANEWARRAY] = MANA_INSN;
b[WIDE] = WIDE_INSN; // WIDE
// runtime-specific
b[LDC_QUICK] = LDC_INSN; // = 203;
b[LDC_W_QUICK] = LDCW_INSN; // = 204;
b[LDC2_W_QUICK] = LDCW_INSN; // = 205;
b[GETFIELD_QUICK] = FIELDORMETH_INSN; // = 206;
b[PUTFIELD_QUICK] = FIELDORMETH_INSN; // = 207;
b[GETFIELD2_QUICK] = FIELDORMETH_INSN; // = 208;
b[PUTFIELD2_QUICK] = FIELDORMETH_INSN; // = 209;
b[GETSTATIC_QUICK] = FIELDORMETH_INSN; // = 210;
b[PUTSTATIC_QUICK] = FIELDORMETH_INSN; // = 211;
b[GETSTATIC2_QUICK] = FIELDORMETH_INSN; // = 212;
b[PUTSTATIC2_QUICK] = FIELDORMETH_INSN; // = 213;
b[INVOKEVIRTUAL_QUICK] = FIELDORMETH_INSN; // = 214;
b[INVOKENONVIRTUAL_QUICK] = FIELDORMETH_INSN; // = 215;
b[INVOKESUPER_QUICK] = FIELDORMETH_INSN; // = 216;
b[INVOKESTATIC_QUICK] = FIELDORMETH_INSN; // = 217;
b[INVOKEINTERFACE_QUICK] = ITFMETH_INSN; // = 218;
b[INVOKEVIRTUALOBJECT_QUICK] = FIELDORMETH_INSN; // = 219;
b[220] = NOARG_INSN; // the ID is not used for any opcode
b[NEW_QUICK] = TYPE_INSN; // = 221;
b[ANEWARRAY_QUICK] = TYPE_INSN; // = 222;
b[MULTIANEWARRAY_QUICK] = MANA_INSN; // = 223;
b[CHECKCAST_QUICK] = TYPE_INSN; // = 224;
b[INSTANCEOF_QUICK] = TYPE_INSN; // = 225;
b[INVOKEVIRTUAL_QUICK_W] = FIELDORMETH_INSN; // = 226;
b[GETFIELD_QUICK_W] = FIELDORMETH_INSN; // = 227;
b[PUTFIELD_QUICK_W] = FIELDORMETH_INSN; // = 228;
for (int aB : b) {
System.err.print((char)('A' + aB));
}
System.err.println();
}
}
@@ -1,229 +0,0 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.debugger.jdi;
import org.jetbrains.annotations.Nullable;
/**
* "Code" attribute parsing extracted from ASM4 library and adopted to finding operations with local variables
*/
public class InstructionParser {
private final byte[] myCode;
private final long myStopOffset;
public InstructionParser(byte[] code, long stopOffset) {
myCode = code;
myStopOffset = stopOffset;
}
public void parse() {
int v = 0;
while (v < myStopOffset) {
int opcode = myCode[v] & 0xFF;
final byte opcodeType = opcode == Bytecodes.IMPDEP1 || opcode == Bytecodes.IMPDEP2? Bytecodes.NOARG_INSN : Bytecodes.TYPE[opcode];
switch (opcodeType) {
case Bytecodes.NOARG_INSN:
v += 1;
break;
case Bytecodes.IMPLVAR_INSN: {
final int varOpcode;
if (opcode > Bytecodes.ISTORE) {
opcode -= Bytecodes.ISTORE_0;
varOpcode = Bytecodes.ISTORE + (opcode >> 2);
}
else {
opcode -= Bytecodes.ILOAD_0;
varOpcode = Bytecodes.ILOAD + (opcode >> 2);
}
final String signature = getVarInstructionTypeSignature(varOpcode);
if (signature != null) {
localVariableInstructionFound(varOpcode, opcode & 0x3, signature);
}
v += 1;
break;
}
case Bytecodes.LABEL_INSN:
v += 3;
break;
case Bytecodes.LABELW_INSN:
v += 5;
break;
case Bytecodes.WIDE_INSN: {
opcode = myCode[v + 1] & 0xFF;
final String signature = getVarInstructionTypeSignature(opcode);
if (signature != null) {
localVariableInstructionFound(opcode, readUnsignedShort(v + 2), signature);
}
if (opcode == Bytecodes.IINC) {
v += 6;
}
else {
v += 4;
}
break;
}
case Bytecodes.TABL_INSN:
// skips 0 to 3 padding bytes
v = v + 4 - (v & 3);
// reads instruction
int min = readInt(v + 4);
int max = readInt(v + 8);
v += 12;
final int length = max - min + 1;
v += 4 * length;
break;
case Bytecodes.LOOK_INSN:
// skips 0 to 3 padding bytes
v = v + 4 - (v & 3);
// reads instruction
final int len = readInt(v + 4);
v += 8 * (len + 1);
break;
case Bytecodes.VAR_INSN: {
final String signature = getVarInstructionTypeSignature(opcode);
if (signature != null) {
localVariableInstructionFound(opcode, myCode[v + 1] & 0xFF, signature);
}
v += 2;
break;
}
case Bytecodes.SBYTE_INSN:
v += 2;
break;
case Bytecodes.SHORT_INSN:
v += 3;
break;
case Bytecodes.LDC_INSN:
v += 2;
break;
case Bytecodes.LDCW_INSN:
v += 3;
break;
case Bytecodes.FIELDORMETH_INSN:
case Bytecodes.ITFMETH_INSN: {
if (opcode == Bytecodes.INVOKEINTERFACE) {
v += 5;
}
else {
v += 3;
}
break;
}
case Bytecodes.INDYMETH_INSN: {
v += 5;
break;
}
case Bytecodes.TYPE_INSN:
v += 3;
break;
case Bytecodes.IINC_INSN: {
final String signature = getVarInstructionTypeSignature(opcode);
if (signature != null) {
localVariableInstructionFound(opcode, myCode[v + 1] & 0xFF, signature);
}
v += 3;
break;
}
// case MANA_INSN:
default:
v += 4;
break;
}
}
}
private int readUnsignedShort(final int index) {
final byte[] b = myCode;
return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF);
}
public int readInt(final int index) {
final byte[] b = myCode;
return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16)
| ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF);
}
protected void localVariableInstructionFound(int opcode, int slot, String typeSignature) {
// override to perform actions
}
@Nullable
private static String getVarInstructionTypeSignature(int opcode) {
switch (opcode) {
case Bytecodes.ILOAD:
case Bytecodes.ILOAD_0:
case Bytecodes.ILOAD_1:
case Bytecodes.ILOAD_2:
case Bytecodes.ILOAD_3:
case Bytecodes.ISTORE:
case Bytecodes.ISTORE_0:
case Bytecodes.ISTORE_1:
case Bytecodes.ISTORE_2:
case Bytecodes.ISTORE_3:
case Bytecodes.IINC:
return "I";
case Bytecodes.LLOAD:
case Bytecodes.LLOAD_0:
case Bytecodes.LLOAD_1:
case Bytecodes.LLOAD_2:
case Bytecodes.LLOAD_3:
case Bytecodes.LSTORE:
case Bytecodes.LSTORE_0:
case Bytecodes.LSTORE_1:
case Bytecodes.LSTORE_2:
case Bytecodes.LSTORE_3:
return "J"; // long
case Bytecodes.FLOAD:
case Bytecodes.FLOAD_0:
case Bytecodes.FLOAD_1:
case Bytecodes.FLOAD_2:
case Bytecodes.FLOAD_3:
case Bytecodes.FSTORE:
case Bytecodes.FSTORE_0:
case Bytecodes.FSTORE_1:
case Bytecodes.FSTORE_2:
case Bytecodes.FSTORE_3:
return "F"; // float
case Bytecodes.DLOAD:
case Bytecodes.DLOAD_0:
case Bytecodes.DLOAD_1:
case Bytecodes.DLOAD_2:
case Bytecodes.DLOAD_3:
case Bytecodes.DSTORE:
case Bytecodes.DSTORE_0:
case Bytecodes.DSTORE_1:
case Bytecodes.DSTORE_2:
case Bytecodes.DSTORE_3:
return "D"; // double
case Bytecodes.ALOAD:
case Bytecodes.ALOAD_0:
case Bytecodes.ALOAD_1:
case Bytecodes.ALOAD_2:
case Bytecodes.ALOAD_3:
case Bytecodes.ASTORE:
case Bytecodes.ASTORE_0:
case Bytecodes.ASTORE_1:
case Bytecodes.ASTORE_2:
case Bytecodes.ASTORE_3:
return "L"; // object ref
}
return null;
}
}
@@ -31,6 +31,8 @@ import com.intellij.util.ReflectionUtil;
import com.intellij.util.containers.MultiMap;
import com.sun.jdi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.Opcodes;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
@@ -148,7 +150,8 @@ public class LocalVariablesUtil {
public static Map<DecompiledLocalVariable, Value> fetchValues(@NotNull StackFrameProxyImpl frameProxy, DebugProcess process) throws Exception {
Map<DecompiledLocalVariable, Value> map = new LinkedHashMap<>(); // LinkedHashMap for correct order
com.sun.jdi.Method method = frameProxy.location().method();
Location location = frameProxy.location();
com.sun.jdi.Method method = location.method();
final int firstLocalVariableSlot = getFirstLocalsSlot(method);
// gather code variables names
@@ -168,7 +171,7 @@ public class LocalVariablesUtil {
}
// now try to fetch stack values
List<DecompiledLocalVariable> vars = collectVariablesFromBytecode(frameProxy, namesMap);
List<DecompiledLocalVariable> vars = collectVariablesFromBytecode(frameProxy.getVirtualMachine(), location, namesMap);
StackFrame frame = frameProxy.getStackFrame();
int size = vars.size();
while (size > 0) {
@@ -266,13 +269,13 @@ public class LocalVariablesUtil {
}
@NotNull
private static List<DecompiledLocalVariable> collectVariablesFromBytecode(StackFrameProxyImpl frame,
final MultiMap<Integer, String> namesMap) {
if (!frame.getVirtualMachine().canGetBytecodes()) {
private static List<DecompiledLocalVariable> collectVariablesFromBytecode(VirtualMachineProxyImpl vm,
Location location,
MultiMap<Integer, String> namesMap) {
if (!vm.canGetBytecodes()) {
return Collections.emptyList();
}
try {
final Location location = frame.location();
LOG.assertTrue(location != null);
final com.sun.jdi.Method method = location.method();
final Location methodLocation = method.location();
@@ -285,19 +288,20 @@ public class LocalVariablesUtil {
if (bytecodes != null && bytecodes.length > 0) {
final int firstLocalVariableSlot = getFirstLocalsSlot(method);
final HashMap<Integer, DecompiledLocalVariable> usedVars = new HashMap<>();
new InstructionParser(bytecodes, location.codeIndex()) {
@Override
protected void localVariableInstructionFound(int opcode, int slot, String typeSignature) {
if (slot >= firstLocalVariableSlot) {
DecompiledLocalVariable variable = usedVars.get(slot);
if (variable == null || !typeSignature.equals(variable.getSignature())) {
variable = new DecompiledLocalVariable(slot, false, typeSignature, namesMap.get(slot));
usedVars.put(slot, variable);
}
}
}
}.parse();
MethodBytecodeUtil.visit(location.declaringType(), method, location.codeIndex(),
new MethodVisitor(Opcodes.ASM5) {
@Override
public void visitVarInsn(int opcode, int slot) {
if (slot >= firstLocalVariableSlot) {
DecompiledLocalVariable variable = usedVars.get(slot);
String typeSignature = MethodBytecodeUtil.getVarInstructionType(opcode).getDescriptor();
if (variable == null || !typeSignature.equals(variable.getSignature())) {
variable = new DecompiledLocalVariable(slot, false, typeSignature, namesMap.get(slot));
usedVars.put(slot, variable);
}
}
}
});
if (usedVars.isEmpty()) {
return Collections.emptyList();
}
@@ -310,7 +314,7 @@ public class LocalVariablesUtil {
catch (UnsupportedOperationException ignored) {
}
catch (Exception e) {
LOG.info(e);
LOG.error(e);
}
return Collections.emptyList();
}
@@ -15,15 +15,20 @@
*/
package com.intellij.debugger.jdi;
import com.intellij.util.ThrowableConsumer;
import com.sun.jdi.ClassType;
import com.sun.jdi.InterfaceType;
import com.sun.jdi.Method;
import com.sun.jdi.ReferenceType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.org.objectweb.asm.*;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* @author egor
@@ -35,21 +40,25 @@ public class MethodBytecodeUtil {
/**
* Allows to use ASM MethodVisitor with jdi method bytecode
*/
public static void visit(ClassType classType, Method method, MethodVisitor methodVisitor) {
public static void visit(ReferenceType classType, Method method, MethodVisitor methodVisitor) {
visit(classType, method, method.bytecodes(), methodVisitor);
}
public static void visit(ClassType classType, Method method, long maxOffset, MethodVisitor methodVisitor) {
visit(classType, method, Arrays.copyOf(method.bytecodes(), (int)maxOffset), methodVisitor);
public static void visit(ReferenceType classType, Method method, long maxOffset, MethodVisitor methodVisitor) {
// need to keep the size, otherwise labels array will not be initialized correctly
byte[] originalBytecodes = method.bytecodes();
byte[] bytecodes = new byte[originalBytecodes.length];
System.arraycopy(originalBytecodes, 0, bytecodes, 0, (int)maxOffset);
visit(classType, method, bytecodes, methodVisitor);
}
private static void visit(ClassType classType, Method method, byte[] bytecodes, MethodVisitor methodVisitor) {
private static void visit(ReferenceType type, Method method, byte[] bytecodes, MethodVisitor methodVisitor) {
try {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(bos)) {
dos.writeInt(0xCAFEBABE); // magic
dos.writeInt(Opcodes.V1_8); // version
dos.writeShort(classType.constantPoolCount()); // constant_pool_count
dos.write(classType.constantPool()); // constant_pool
dos.writeShort(type.constantPoolCount()); // constant_pool_count
dos.write(type.constantPool()); // constant_pool
dos.writeShort(0); // access_flags;
dos.writeShort(0); // this_class;
dos.writeShort(0); // super_class;
@@ -58,17 +67,31 @@ public class MethodBytecodeUtil {
dos.writeShort(0); // methods_count;
dos.writeShort(0); // attributes_count;
ClassWriter clsWriter = new ClassWriter(new ClassReader(bos.toByteArray()), 0);
clsWriter.visit(Opcodes.V1_8,
Opcodes.ACC_PUBLIC,
classType.name(),
classType.signature(),
classType.superclass().name(),
classType.interfaces().stream().map(ReferenceType::name).toArray(String[]::new));
MethodVisitor mv = clsWriter.visitMethod(Opcodes.ACC_PUBLIC, method.name(), method.signature(), method.signature(), null);
ClassReader reader = new ClassReader(bos.toByteArray());
ClassWriter writer = new ClassWriter(reader, 0);
String superName = null;
String[] interfaces = null;
if (type instanceof ClassType) {
ClassType classType = (ClassType)type;
ClassType superClass = classType.superclass();
superName = superClass != null ? superClass.name() : null;
interfaces = classType.interfaces().stream().map(ReferenceType::name).toArray(String[]::new);
}
else if (type instanceof InterfaceType) {
interfaces = ((InterfaceType)type).superinterfaces().stream().map(ReferenceType::name).toArray(String[]::new);
}
writer.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, type.name(), type.signature(), superName, interfaces);
Attribute bootstrapMethods = createBootstrapMethods(reader, writer);
if (bootstrapMethods != null) {
writer.visitAttribute(bootstrapMethods);
}
MethodVisitor mv = writer.visitMethod(Opcodes.ACC_PUBLIC, method.name(), method.signature(), method.signature(), null);
mv.visitAttribute(createCode(bytecodes));
new ClassReader(clsWriter.toByteArray()).accept(new ClassVisitor(Opcodes.ASM5) {
new ClassReader(writer.toByteArray()).accept(new ClassVisitor(Opcodes.ASM5) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
assert name.equals(method.name());
@@ -81,30 +104,96 @@ public class MethodBytecodeUtil {
}
}
private static Attribute createCode(byte[] bytecodes) throws IOException {
@NotNull
private static Attribute createAttribute(String name, ThrowableConsumer<DataOutputStream, IOException> generator) throws IOException {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(bos)) {
dos.writeInt(0xCAFEBABE); // magic
dos.writeInt(Opcodes.V1_8); // version
dos.writeShort(0); // constant_pool_count
// we generate and put code attribute right after the constant pool
int codeSize = dos.size();
// we generate and put attribute right after the constant pool
int attributeSize = dos.size();
generator.consume(dos);
attributeSize = dos.size() - attributeSize;
ClassReader cr = new ClassReader(bos.toByteArray());
return new Attribute(name) {
@Override
public Attribute read(ClassReader cr, int off, int len, char[] buf, int codeOff, Label[] labels) {
return super.read(cr, off, len, buf, codeOff, labels);
}
}.read(cr, cr.header, attributeSize, null, 0, null);
}
}
@Nullable
private static Attribute createBootstrapMethods(ClassReader classReader, ClassWriter classWriter) throws IOException {
Set<Short> indys = new HashSet<>();
// scan class pool for indy calls
for (int i = 1; i < classReader.getItemCount(); i++) {
int index = classReader.getItem(i);
int tag = classReader.readByte(index - 1);
switch (tag) {
case 5: // ClassWriter.LONG
case 6: // ClassWriter.DOUBLE
++i;
break;
case 18: // ClassWriter.INDY
indys.add(classReader.readShort(index));
//short methodIndex = classReader.readShort(index);
short nameTypeIndex = classReader.readShort(index + 2);
}
}
if (!indys.isEmpty()) {
int dummyRef = classWriter.newHandle(Opcodes.H_INVOKESTATIC, "DummyOwner", "DummyMethod", "", false); // dummy for now
return createAttribute("BootstrapMethods", dos -> {
dos.writeShort(indys.size());
for (Short indy : indys) {
dos.writeShort(dummyRef); // bootstrap_method_ref
dos.writeShort(0); // num_bootstrap_arguments
//dos.writeShort(0); // bootstrap_arguments
}
});
}
return null;
}
@NotNull
private static Attribute createCode(byte[] bytecodes) throws IOException {
return createAttribute("Code", dos -> {
dos.writeShort(0); // max_stack
dos.writeShort(0); // max_locals
dos.writeInt(bytecodes.length); // code_length
dos.write(bytecodes); // code
dos.writeShort(0); // exception_table_length
dos.writeShort(0); // attributes_count
codeSize = dos.size() - codeSize;
});
}
ClassReader cr = new ClassReader(bos.toByteArray());
private static final Type OBJECT_TYPE = Type.getObjectType("java/lang/Object");
return new Attribute("Code") {
@Override
public Attribute read(ClassReader cr, int off, int len, char[] buf, int codeOff, Label[] labels) {
return super.read(cr, off, len, buf, codeOff, labels);
}
}.read(cr, cr.header, codeSize, null, 0, null);
public static Type getVarInstructionType(int opcode) {
switch (opcode) {
case Opcodes.LLOAD:
case Opcodes.LSTORE:
return Type.LONG_TYPE;
case Opcodes.DLOAD:
case Opcodes.DSTORE:
return Type.DOUBLE_TYPE;
case Opcodes.FLOAD:
case Opcodes.FSTORE:
return Type.FLOAT_TYPE;
case Opcodes.ILOAD:
case Opcodes.ISTORE:
return Type.INT_TYPE;
default:
// case Opcodes.ALOAD:
// case Opcodes.ASTORE:
// case RET:
return OBJECT_TYPE;
}
}
}