if (code == OpCode.WIDE) {
code = code << 8 | b.readUnsignedByte();
}
final OpCode op = OpCode.get(code);
final Instruction instruction;
switch (op.getOperandType()) {
case None: {
if (op.isLoad() || op.isStore()) {
variables.ensure(OpCodeHelpers.getLoadStoreMacroArgumentIndex(op), op, offset);
}
instruction = Instruction.create(op);
break;
}
case PrimitiveTypeCode: {
instruction = Instruction.create(op, BuiltinTypes.fromPrimitiveTypeCode(b.readUnsignedByte()));
break;
}
case TypeReference: {
final int typeToken = b.readUnsignedShort();
instruction = Instruction.create(op, _scope.lookupType(typeToken));
break;
}
case TypeReferenceU1: {
instruction = Instruction.create(op, _scope.lookupType(b.readUnsignedShort()), b.readUnsignedByte());
break;
}
case DynamicCallSite: {
instruction = Instruction.create(op, _scope.lookupDynamicCallSite(b.readUnsignedShort()));
b.readUnsignedByte();
b.readUnsignedByte();
break;
}
case MethodReference: {
instruction = Instruction.create(op, _scope.lookupMethod(b.readUnsignedShort()));
if (op == OpCode.INVOKEINTERFACE) {
b.readUnsignedByte();
b.readUnsignedByte();
}
break;
}
case FieldReference: {
instruction = Instruction.create(op, _scope.lookupField(b.readUnsignedShort()));
break;
}
case BranchTarget: {
final int targetOffset;
instruction = new Instruction(op);
if (op.isWide()) {
targetOffset = offset + _scope.<Integer>lookupConstant(b.readUnsignedShort());
}
else {
targetOffset = offset + (int) b.readShort();
}
if (targetOffset < offset) {
final Instruction target = body.atOffset(targetOffset);
if (!target.hasLabel()) {
target.setLabel(new Label(targetOffset));
}
instruction.setOperand(target);
}
else if (targetOffset == offset) {
instruction.setOperand(instruction);
instruction.setLabel(new Label(offset));
}
else if (targetOffset > b.size()) {
//
// Target is a label after the last instruction. Insert a dummy NOP.
//
instruction.setOperand(new Instruction(targetOffset, OpCode.NOP));
}
else {
final Fixup oldFixup = fixups[targetOffset];
final Fixup newFixup = new Fixup() {
@Override
public void fix(final Instruction target) {
instruction.setOperand(target);
}
};
fixups[targetOffset] = oldFixup != null ? Fixup.combine(oldFixup, newFixup)
: newFixup;
}
break;
}
case I1: {
instruction = Instruction.create(op, b.readByte());
break;
}
case I2: {
instruction = Instruction.create(op, b.readShort());
break;
}
case I8: {
instruction = Instruction.create(op, b.readLong());
break;
}
case Constant: {
instruction = new Instruction(op, _scope.lookupConstant(b.readUnsignedByte()));
break;
}
case WideConstant: {
final int constantToken = b.readUnsignedShort();
instruction = new Instruction(op, _scope.lookupConstant(constantToken));
break;
}
case Switch: {
while (b.position() % 4 != 0) {
b.readByte();
}
final SwitchInfo switchInfo = new SwitchInfo();
final int defaultOffset = offset + b.readInt();
instruction = Instruction.create(op, switchInfo);
if (defaultOffset < offset) {
switchInfo.setDefaultTarget(body.atOffset(defaultOffset));
}
else if (defaultOffset == offset) {
switchInfo.setDefaultTarget(instruction);
}
else {
switchInfo.setDefaultTarget(new Instruction(defaultOffset, OpCode.NOP));
final Fixup oldFixup = fixups[defaultOffset];
final Fixup newFixup = new Fixup() {
@Override
public void fix(final Instruction target) {
switchInfo.setDefaultTarget(target);
}
};
fixups[defaultOffset] = oldFixup != null ? Fixup.combine(oldFixup, newFixup)
: newFixup;
}
if (op == OpCode.TABLESWITCH) {
final int low = b.readInt();
final int high = b.readInt();
final Instruction[] targets = new Instruction[high - low + 1];
switchInfo.setLowValue(low);
switchInfo.setHighValue(high);
for (int i = 0; i < targets.length; i++) {
final int targetIndex = i;
final int targetOffset = offset + b.readInt();
if (targetOffset < offset) {
targets[targetIndex] = body.atOffset(targetOffset);
}
else if (targetOffset == offset) {
targets[targetIndex] = instruction;
}
else {
targets[targetIndex] = new Instruction(targetOffset, OpCode.NOP);
final Fixup oldFixup = fixups[targetOffset];
final Fixup newFixup = new Fixup() {
@Override
public void fix(final Instruction target) {
targets[targetIndex] = target;
}
};
fixups[targetOffset] = oldFixup != null ? Fixup.combine(oldFixup, newFixup)
: newFixup;
}
}
switchInfo.setTargets(targets);
}
else {
final int pairCount = b.readInt();
final int[] keys = new int[pairCount];
final Instruction[] targets = new Instruction[pairCount];
for (int i = 0; i < pairCount; i++) {
final int targetIndex = i;
keys[targetIndex] = b.readInt();
final int targetOffset = offset + b.readInt();
if (targetOffset < offset) {
targets[targetIndex] = body.atOffset(targetOffset);
}
else if (targetOffset == offset) {
targets[targetIndex] = instruction;
}
else {
targets[targetIndex] = new Instruction(targetOffset, OpCode.NOP);
final Fixup oldFixup = fixups[targetOffset];
final Fixup newFixup = new Fixup() {
@Override
public void fix(final Instruction target) {
targets[targetIndex] = target;
}
};
fixups[targetOffset] = oldFixup != null ? Fixup.combine(oldFixup, newFixup)
: newFixup;
}
}
switchInfo.setKeys(keys);
switchInfo.setTargets(targets);
}
break;
}
case Local: {
final int variableSlot;
if (op.isWide()) {
variableSlot = b.readUnsignedShort();
}
else {
variableSlot = b.readUnsignedByte();
}
final VariableDefinition variable = variables.ensure(variableSlot, op, offset);
if (variableSlot < 0) {
instruction = new Instruction(op, new ErrorOperand("!!! BAD LOCAL: " + variableSlot + " !!!"));
}
else {
instruction = Instruction.create(op, variable);
}
break;
}
case LocalI1: {
final int variableSlot;
final int operand;
if (op.isWide()) {
variableSlot = b.readUnsignedShort();
}
else {
variableSlot = b.readUnsignedByte();
}
final VariableDefinition variable = variables.ensure(variableSlot, op, offset);
operand = b.readByte();
if (variableSlot < 0) {
instruction = new Instruction(
op,
new ErrorOperand("!!! BAD LOCAL: " + variableSlot + " !!!"),
operand
);
}
else {
instruction = Instruction.create(op, variable, operand);
}
break;
}
case LocalI2: {
final int variableSlot;
final int operand;
if (op.isWide()) {
variableSlot = b.readUnsignedShort();
}
else {
variableSlot = b.readUnsignedByte();
}
final VariableDefinition variable = variables.ensure(variableSlot, op, offset);
operand = b.readShort();
if (variableSlot < 0) {
instruction = new Instruction(
op,
new ErrorOperand("!!! BAD LOCAL: " + variableSlot + " !!!"),
operand
);
}
else {
instruction = Instruction.create(op, variable, operand);
}
break;
}
default: {
throw new IllegalStateException("Unrecognized opcode: " + code);
}
}
instruction.setOffset(offset);
body.add(instruction);
final Fixup fixup = fixups[offset];
if (fixup != null) {
if (!instruction.hasLabel()) {
instruction.setLabel(new Label(offset));
}
fixup.fix(instruction);
}
}
variables.updateScopes(_code.getCodeSize());
variables.mergeVariables();
variables.updateScopes(_code.getCodeSize());
int labelCount = 0;
for (int i = 0; i < body.size(); i++) {
final Instruction instruction = body.get(i);
final OpCode code = instruction.getOpCode();
final Object operand = instruction.hasOperand() ? instruction.getOperand(0) : null;
if (operand instanceof VariableDefinition) {
final VariableDefinition currentVariable = (VariableDefinition) operand;
final int effectiveOffset;
if (code.isStore()) {
effectiveOffset = instruction.getOffset() + code.getSize() + code.getOperandType().getBaseSize();
}
else {
effectiveOffset = instruction.getOffset();
}
VariableDefinition actualVariable = variables.tryFind(currentVariable.getSlot(), effectiveOffset);
if (actualVariable == null && code.isStore()) {
actualVariable = variables.find(
currentVariable.getSlot(),
effectiveOffset + code.getSize() + code.getOperandType().getBaseSize()
);
}
if (actualVariable != currentVariable) {
if (instruction.getOperandCount() > 1) {