blob: 78bf10fe7e407634ab2b25bd7e1108e9858e7964 [file] [log] [blame]
/*
* 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;
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;
}
}