IDEA-114593 Debugger: Show variable information when no debug info

This commit is contained in:
Eugene Zhuravlev
2013-10-08 17:02:53 +02:00
parent 1918d6ea55
commit 2624ec616c
9 changed files with 1049 additions and 13 deletions
@@ -18,19 +18,23 @@ package com.intellij.debugger.impl.descriptors.data;
import com.intellij.debugger.ui.impl.watch.ArgumentValueDescriptorImpl;
import com.intellij.openapi.project.Project;
import com.sun.jdi.Value;
import org.jetbrains.annotations.Nullable;
public class ArgValueData extends DescriptorData<ArgumentValueDescriptorImpl>{
private final int myIndex;
private final Value myValue;
@Nullable
private final String myDisplayName;
public ArgValueData(int index, Value value) {
public ArgValueData(int index, Value value, @Nullable String displayName) {
super();
myIndex = index;
myValue = value;
myDisplayName = displayName;
}
protected ArgumentValueDescriptorImpl createDescriptorImpl(Project project) {
return new ArgumentValueDescriptorImpl(project, myIndex, myValue);
return new ArgumentValueDescriptorImpl(project, myIndex, myValue, myDisplayName);
}
public boolean equals(Object object) {
@@ -0,0 +1,449 @@
/*
* Copyright 2000-2013 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 = "AAAAAAAAAAAAAAAABCLMMDDDDDEEEEEEEEEEEEEEEEEEEEAAAAAAAADD"
+ "DDDEEEEEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAANAAAAAAAAAAAAAAAAAAAAJJJJJJJJJJJJJJJJDOPAA"
+ "AAAAGGGGGGGHIFBFAAFFAARQJJKKJJJJJJJJJJJJJJJJJJ";
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; // -
}
@@ -0,0 +1,53 @@
/*
* Copyright 2000-2013 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 java.util.Comparator;
/**
* @author Eugene Zhuravlev
* Date: 10/7/13
*/
public class DecompiledLocalVariable{
public static final Comparator<DecompiledLocalVariable> COMPARATOR = new Comparator<DecompiledLocalVariable>() {
@Override
public int compare(DecompiledLocalVariable v1, DecompiledLocalVariable v2) {
return v1.getSlot() - v2.getSlot();
}
};
private final int mySlot;
private final String mySignature;
private final String myName;
public DecompiledLocalVariable(int slot, String name, String signature) {
mySlot = slot;
mySignature = signature;
myName = name;
}
public int getSlot() {
return mySlot;
}
public String getSignature() {
return mySignature;
}
public String getName() {
return myName;
}
}
@@ -0,0 +1,233 @@
/*
* Copyright 2000-2013 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 myCurrentInstructionIndex;
public InstructionParser(byte[] code, long instructionIndex) {
myCode = code;
myCurrentInstructionIndex = instructionIndex;
}
public void parse() {
final int codeEnd = myCode.length;
int v = 0;
int instructionIndex = 0;
while (v < codeEnd) {
if (instructionIndex++ >= myCurrentInstructionIndex) {
break;
}
int opcode = myCode[v] & 0xFF;
switch (Bytecodes.TYPE[opcode]) {
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;
}
}
@@ -0,0 +1,178 @@
/*
* Copyright 2000-2013 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 com.intellij.openapi.diagnostic.Logger;
import com.sun.jdi.InternalException;
import com.sun.jdi.StackFrame;
import com.sun.jdi.Value;
import com.sun.jdi.VirtualMachine;
import java.lang.reflect.*;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* From JDI sources:
*
validateStackFrame();
validateMirrors(variables);
int count = variables.size();
JDWP.StackFrame.GetValues.SlotInfo[] slots =
new JDWP.StackFrame.GetValues.SlotInfo[count];
for (int i=0; i<count; ++i) {
LocalVariableImpl variable = (LocalVariableImpl)variables.get(i);
if (!variable.isVisible(this)) {
throw new IllegalArgumentException(variable.name() +
" is not valid at this frame location");
}
slots[i] = new JDWP.StackFrame.GetValues.SlotInfo(variable.slot(),
(byte)variable.signature().charAt(0));
}
PacketStream ps;
synchronized (vm.state()) {
validateStackFrame();
ps = JDWP.StackFrame.GetValues.enqueueCommand(vm, thread, id, slots);
}
ValueImpl[] values;
try {
values = JDWP.StackFrame.GetValues.waitForReply(vm, ps).values;
} catch (JDWPException exc) {
switch (exc.errorCode()) {
case JDWP.Error.INVALID_FRAMEID:
case JDWP.Error.THREAD_NOT_SUSPENDED:
case JDWP.Error.INVALID_THREAD:
throw new InvalidStackFrameException();
default:
throw exc.toJDIException();
}
}
if (count != values.length) {
throw new InternalException(
"Wrong number of values returned from target VM");
}
Map map = new HashMap(count);
for (int i=0; i<count; ++i) {
LocalVariableImpl variable = (LocalVariableImpl)variables.get(i);
map.put(variable, values[i]);
}
return map;
*/
public class LocalVariablesUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.jdi.LocalVariablesUtil");
private static final boolean ourInitializationOk;
private static Class<?> ourSlotInfoClass;
private static Constructor<?> slotInfoConstructor;
private static Class<?> ourGetValuesClass;
private static Method ourEnqueueMethod;
private static Method ourWaitForReplyMethod;
static {
boolean success = false;
try {
ourSlotInfoClass = Class.forName("com.sun.tools.jdi.JDWP$StackFrame$GetValues$SlotInfo");
slotInfoConstructor = ourSlotInfoClass.getDeclaredConstructor(int.class, byte.class);
slotInfoConstructor.setAccessible(true);
ourGetValuesClass = Class.forName("com.sun.tools.jdi.JDWP$StackFrame$GetValues");
ourEnqueueMethod = findMethod(ourGetValuesClass, "enqueueCommand");
ourEnqueueMethod.setAccessible(true);
ourWaitForReplyMethod = findMethod(ourGetValuesClass, "waitForReply");
ourWaitForReplyMethod.setAccessible(true);
success = true;
}
catch (Throwable e) {
LOG.info(e);
}
ourInitializationOk = success;
}
public static Map<DecompiledLocalVariable, Value> fetchValues(StackFrame frame, Collection<DecompiledLocalVariable> vars) throws Exception {
if (!ourInitializationOk) {
return Collections.emptyMap();
}
final Field frameIdField = frame.getClass().getDeclaredField("id");
frameIdField.setAccessible(true);
final Object frameId = frameIdField.get(frame);
final VirtualMachine vm = frame.virtualMachine();
final Method stateMethod = vm.getClass().getDeclaredMethod("state");
stateMethod.setAccessible(true);
Object slotInfoArray = createSlotInfoArray(vars);
Object ps;
final Object vmState = stateMethod.invoke(vm);
synchronized(vmState) {
ps = ourEnqueueMethod.invoke(null, vm, frame.thread(), frameId, slotInfoArray);
}
try {
final Object reply = ourWaitForReplyMethod.invoke(null, vm, ps);
final Field valuesField = reply.getClass().getDeclaredField("values");
valuesField.setAccessible(true);
final Value[] values = (Value[])valuesField.get(reply);
if (vars.size() != values.length) {
throw new InternalException("Wrong number of values returned from target VM");
}
final Map<DecompiledLocalVariable, Value> map = new HashMap<DecompiledLocalVariable, Value>(vars.size());
int idx = 0;
for (DecompiledLocalVariable var : vars) {
map.put(var, values[idx++]);
}
return map;
}
catch (InvocationTargetException e) {
final Throwable target = e.getTargetException();
if (target instanceof Exception) {
throw (Exception)target;
}
throw e;
}
}
private static Object createSlotInfoArray(Collection<DecompiledLocalVariable> vars) throws Exception {
final Object arrayInstance = Array.newInstance(ourSlotInfoClass, vars.size());
int idx = 0;
for (DecompiledLocalVariable var : vars) {
final Object info = slotInfoConstructor.newInstance(var.getSlot(), (byte)var.getSignature().charAt(0));
Array.set(arrayInstance, idx++, info);
}
return arrayInstance;
}
private static Method findMethod(Class aClass, String methodName) throws NoSuchMethodException {
for (Method method : aClass.getDeclaredMethods()) {
if (methodName.equals(method.getName())) {
return method;
}
}
throw new NoSuchMethodException(aClass.getName() + "." + methodName);
}
}
@@ -30,11 +30,10 @@ import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.engine.evaluation.TextWithImports;
import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
import com.intellij.debugger.engine.events.DebuggerCommandImpl;
import com.intellij.debugger.engine.jdi.StackFrameProxy;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.impl.DebuggerSession;
import com.intellij.debugger.jdi.LocalVariableProxyImpl;
import com.intellij.debugger.jdi.StackFrameProxyImpl;
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
import com.intellij.debugger.jdi.*;
import com.intellij.debugger.settings.ViewsGeneralSettings;
import com.intellij.debugger.ui.impl.watch.*;
import com.intellij.lang.java.JavaLanguage;
@@ -53,9 +52,9 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.text.CharArrayUtil;
import com.intellij.util.ui.tree.TreeModelAdapter;
import com.intellij.xdebugger.XDebuggerBundle;
import com.sun.jdi.AbsentInformationException;
import com.sun.jdi.ObjectCollectedException;
import com.sun.jdi.Value;
import com.sun.jdi.*;
import gnu.trove.TIntObjectHashMap;
import gnu.trove.TObjectProcedure;
import javax.swing.event.TreeModelEvent;
import javax.swing.tree.TreeModel;
@@ -167,10 +166,25 @@ public class FrameVariablesTree extends DebuggerTree {
final Collection<Value> argValues = frame.getArgumentValues();
int index = 0;
for (Value argValue : argValues) {
final ArgumentValueDescriptorImpl descriptor = myNodeManager.getArgumentValueDescriptor(stackDescriptor, index++, argValue);
final ArgumentValueDescriptorImpl descriptor = myNodeManager.getArgumentValueDescriptor(stackDescriptor, index++, argValue, null);
final DebuggerTreeNodeImpl variableNode = myNodeManager.createNode(descriptor, evaluationContext);
myChildren.add(variableNode);
}
final List<DecompiledLocalVariable> decompiled = collectVariablesFromBytecode(frame, argValues.size());
if (!decompiled.isEmpty()) {
try {
final Map<DecompiledLocalVariable, Value> values = LocalVariablesUtil.fetchValues(frame.getStackFrame(), decompiled);
for (DecompiledLocalVariable var : decompiled) {
final Value value = values.get(var);
final ArgumentValueDescriptorImpl descriptor = myNodeManager.getArgumentValueDescriptor(stackDescriptor, var.getSlot(), value, var.getName());
final DebuggerTreeNodeImpl variableNode = myNodeManager.createNode(descriptor, evaluationContext);
myChildren.add(variableNode);
}
}
catch (Exception ex) {
LOG.info(ex);
}
}
myChildren.add(myNodeManager.createMessageNode(MessageDescriptor.LOCAL_VARIABLES_INFO_UNAVAILABLE));
}
else {
@@ -180,6 +194,56 @@ public class FrameVariablesTree extends DebuggerTree {
}
}
private static List<DecompiledLocalVariable> collectVariablesFromBytecode(final StackFrameProxy frame, int argumentCount) throws EvaluateException {
if (!frame.getVirtualMachine().canGetBytecodes()) {
return Collections.emptyList();
}
try {
final Location location = frame.location();
final Method method = location.method();
final Location methodLocation = method.location();
if (methodLocation == null || methodLocation.codeIndex() < 0) {
// native or abstract method
return Collections.emptyList();
}
final byte[] bytecodes = method.bytecodes();
if (bytecodes != null && bytecodes.length > 0) {
final int firstLocalVariableSlot = argumentCount + (method.isStatic()? 0 : 1);
final long instructionIndex = location.codeIndex();
final TIntObjectHashMap<DecompiledLocalVariable> usedVars = new TIntObjectHashMap<DecompiledLocalVariable>();
new InstructionParser(bytecodes, instructionIndex) {
@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, "slot_" + slot, typeSignature);
usedVars.put(slot, variable);
}
}
}
}.parse();
if (usedVars.isEmpty()) {
return Collections.emptyList();
}
final List<DecompiledLocalVariable> vars = new ArrayList<DecompiledLocalVariable>(usedVars.size());
usedVars.forEachValue(new TObjectProcedure<DecompiledLocalVariable>() {
@Override
public boolean execute(DecompiledLocalVariable var) {
vars.add(var);
return true;
}
});
Collections.sort(vars, DecompiledLocalVariable.COMPARATOR);
return vars;
}
}
catch (UnsupportedOperationException ignored) {
}
return Collections.emptyList();
}
private static Map<String, LocalVariableProxyImpl> getVisibleVariables(final StackFrameDescriptorImpl stackDescriptor) throws EvaluateException {
final StackFrameProxyImpl frame = stackDescriptor.getFrameProxy();
if (frame == null) {
@@ -30,17 +30,21 @@ import com.intellij.util.IncorrectOperationException;
import com.sun.jdi.PrimitiveValue;
import com.sun.jdi.Value;
import java.util.Stack;
public class ArgumentValueDescriptorImpl extends ValueDescriptorImpl{
private final int myIndex;
private final Value myValue;
private String myName;
private boolean myParameterNameCalcutated;
private final String myDefaultName;
public ArgumentValueDescriptorImpl(Project project, int index, Value value) {
public ArgumentValueDescriptorImpl(Project project, int index, Value value, String name) {
super(project);
myIndex = index;
myValue = value;
myName = "arg" + String.valueOf(index);
myDefaultName = name != null ? name : "arg" + String.valueOf(index);
myName = myDefaultName;
setLvalue(true);
}
@@ -61,6 +65,55 @@ public class ArgumentValueDescriptorImpl extends ValueDescriptorImpl{
myName = param.getName();
myParameterNameCalcutated = true;
}
else {
// treat myIndex as a variable slot index
final PsiCodeBlock body = method.getBody();
if (body != null) {
final StringBuilder nameBuilder = new StringBuilder();
nameBuilder.append(myDefaultName);
try {
final int startSlot = params.getParametersCount() + (method.hasModifierProperty(PsiModifier.STATIC)? 0 : 1);
body.accept(new JavaRecursiveElementVisitor() {
private int myCurrentSlotIndex = startSlot;
private final Stack<Integer> myIndexStack = new Stack<Integer>();
@Override
public void visitCodeBlock(PsiCodeBlock block) {
myIndexStack.push(myCurrentSlotIndex);
try {
super.visitCodeBlock(block);
}
finally {
myCurrentSlotIndex = myIndexStack.pop();
}
}
@Override
public void visitLocalVariable(PsiLocalVariable variable) {
if (myCurrentSlotIndex == myIndex) {
if (nameBuilder.length() == myDefaultName.length()) {
nameBuilder.append(": ");
}
else {
nameBuilder.append("|");
}
nameBuilder.append(variable.getName());
}
final PsiType varType = variable.getType();
myCurrentSlotIndex += (varType == PsiType.DOUBLE || varType == PsiType.LONG)? 2 : 1;
}
@Override
public void visitClass(PsiClass aClass) {
// skip local and anonymous classes
}
});
}
finally {
myName = nameBuilder.toString();
}
}
}
}
}
}
@@ -152,8 +152,8 @@ public class NodeDescriptorFactoryImpl implements NodeDescriptorFactory {
return getDescriptor(parent, new LocalData((LocalVariableProxyImpl)local));
}
public ArgumentValueDescriptorImpl getArgumentValueDescriptor(NodeDescriptor parent, int index, Value value) {
return getDescriptor(parent, new ArgValueData(index, value));
public ArgumentValueDescriptorImpl getArgumentValueDescriptor(NodeDescriptor parent, int index, Value value, final String name) {
return getDescriptor(parent, new ArgValueData(index, value, name));
}
public StackFrameDescriptorImpl getStackFrameDescriptor(NodeDescriptorImpl parent, StackFrameProxyImpl frameProxy) {
@@ -26,6 +26,8 @@ import java.util.List;
public interface VirtualMachineProxy {
List<ReferenceType> allClasses();
boolean canGetBytecodes();
boolean versionHigher(String version);
boolean canWatchFieldModification();