/* * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package proguard.evaluation; import proguard.classfile.*; import proguard.classfile.attribute.CodeAttribute; import proguard.classfile.instruction.*; import proguard.classfile.instruction.visitor.InstructionVisitor; import proguard.evaluation.value.*; /** * This InstructionVisitor executes the instructions that it visits on a given * local variable frame and stack. * * @author Eric Lafortune */ public class Processor implements InstructionVisitor { private final Variables variables; private final Stack stack; private final ValueFactory valueFactory; private final BranchUnit branchUnit; private final InvocationUnit invocationUnit; private final boolean alwaysCast; private final ConstantValueFactory constantValueFactory; private final ClassConstantValueFactory classConstantValueFactory; /** * Creates a new processor that operates on the given environment. * @param variables the local variable frame. * @param stack the local stack. * @param branchUnit the class that can affect the program counter. * @param invocationUnit the class that can access other program members. * @param alwaysCast a flag that specifies whether downcasts or casts * of null values should always be performed. */ public Processor(Variables variables, Stack stack, ValueFactory valueFactory, BranchUnit branchUnit, InvocationUnit invocationUnit, boolean alwaysCast) { this.variables = variables; this.stack = stack; this.valueFactory = valueFactory; this.branchUnit = branchUnit; this.invocationUnit = invocationUnit; this.alwaysCast = alwaysCast; constantValueFactory = new ConstantValueFactory(valueFactory); classConstantValueFactory = new ClassConstantValueFactory(valueFactory); } // Implementations for InstructionVisitor. public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) { switch (simpleInstruction.opcode) { case InstructionConstants.OP_NOP: break; case InstructionConstants.OP_ACONST_NULL: stack.push(valueFactory.createReferenceValueNull()); break; case InstructionConstants.OP_ICONST_M1: case InstructionConstants.OP_ICONST_0: case InstructionConstants.OP_ICONST_1: case InstructionConstants.OP_ICONST_2: case InstructionConstants.OP_ICONST_3: case InstructionConstants.OP_ICONST_4: case InstructionConstants.OP_ICONST_5: case InstructionConstants.OP_BIPUSH: case InstructionConstants.OP_SIPUSH: stack.push(valueFactory.createIntegerValue(simpleInstruction.constant)); break; case InstructionConstants.OP_LCONST_0: case InstructionConstants.OP_LCONST_1: stack.push(valueFactory.createLongValue(simpleInstruction.constant)); break; case InstructionConstants.OP_FCONST_0: case InstructionConstants.OP_FCONST_1: case InstructionConstants.OP_FCONST_2: stack.push(valueFactory.createFloatValue((float)simpleInstruction.constant)); break; case InstructionConstants.OP_DCONST_0: case InstructionConstants.OP_DCONST_1: stack.push(valueFactory.createDoubleValue((double)simpleInstruction.constant)); break; case InstructionConstants.OP_IALOAD: case InstructionConstants.OP_BALOAD: case InstructionConstants.OP_CALOAD: case InstructionConstants.OP_SALOAD: { IntegerValue arrayIndex = stack.ipop(); ReferenceValue arrayReference = stack.apop(); stack.push(arrayReference.integerArrayLoad(arrayIndex, valueFactory)); break; } case InstructionConstants.OP_LALOAD: { IntegerValue arrayIndex = stack.ipop(); ReferenceValue arrayReference = stack.apop(); stack.push(arrayReference.longArrayLoad(arrayIndex, valueFactory)); break; } case InstructionConstants.OP_FALOAD: { IntegerValue arrayIndex = stack.ipop(); ReferenceValue arrayReference = stack.apop(); stack.push(arrayReference.floatArrayLoad(arrayIndex, valueFactory)); break; } case InstructionConstants.OP_DALOAD: { IntegerValue arrayIndex = stack.ipop(); ReferenceValue arrayReference = stack.apop(); stack.push(arrayReference.doubleArrayLoad(arrayIndex, valueFactory)); break; } case InstructionConstants.OP_AALOAD: { IntegerValue arrayIndex = stack.ipop(); ReferenceValue arrayReference = stack.apop(); stack.push(arrayReference.referenceArrayLoad(arrayIndex, valueFactory)); break; } case InstructionConstants.OP_IASTORE: case InstructionConstants.OP_BASTORE: case InstructionConstants.OP_CASTORE: case InstructionConstants.OP_SASTORE: { Value value = stack.ipop(); IntegerValue arrayIndex = stack.ipop(); ReferenceValue arrayReference = stack.apop(); arrayReference.arrayStore(arrayIndex, value); break; } case InstructionConstants.OP_LASTORE: { Value value = stack.lpop(); IntegerValue arrayIndex = stack.ipop(); ReferenceValue arrayReference = stack.apop(); arrayReference.arrayStore(arrayIndex, value); break; } case InstructionConstants.OP_FASTORE: { Value value = stack.fpop(); IntegerValue arrayIndex = stack.ipop(); ReferenceValue arrayReference = stack.apop(); arrayReference.arrayStore(arrayIndex, value); break; } case InstructionConstants.OP_DASTORE: { Value value = stack.dpop(); IntegerValue arrayIndex = stack.ipop(); ReferenceValue arrayReference = stack.apop(); arrayReference.arrayStore(arrayIndex, value); break; } case InstructionConstants.OP_AASTORE: { Value value = stack.apop(); IntegerValue arrayIndex = stack.ipop(); ReferenceValue arrayReference = stack.apop(); arrayReference.arrayStore(arrayIndex, value); break; } case InstructionConstants.OP_POP: stack.pop1(); break; case InstructionConstants.OP_POP2: stack.pop2(); break; case InstructionConstants.OP_DUP: stack.dup(); break; case InstructionConstants.OP_DUP_X1: stack.dup_x1(); break; case InstructionConstants.OP_DUP_X2: stack.dup_x2(); break; case InstructionConstants.OP_DUP2: stack.dup2(); break; case InstructionConstants.OP_DUP2_X1: stack.dup2_x1(); break; case InstructionConstants.OP_DUP2_X2: stack.dup2_x2(); break; case InstructionConstants.OP_SWAP: stack.swap(); break; case InstructionConstants.OP_IADD: stack.push(stack.ipop().add(stack.ipop())); break; case InstructionConstants.OP_LADD: stack.push(stack.lpop().add(stack.lpop())); break; case InstructionConstants.OP_FADD: stack.push(stack.fpop().add(stack.fpop())); break; case InstructionConstants.OP_DADD: stack.push(stack.dpop().add(stack.dpop())); break; case InstructionConstants.OP_ISUB: stack.push(stack.ipop().subtractFrom(stack.ipop())); break; case InstructionConstants.OP_LSUB: stack.push(stack.lpop().subtractFrom(stack.lpop())); break; case InstructionConstants.OP_FSUB: stack.push(stack.fpop().subtractFrom(stack.fpop())); break; case InstructionConstants.OP_DSUB: stack.push(stack.dpop().subtractFrom(stack.dpop())); break; case InstructionConstants.OP_IMUL: stack.push(stack.ipop().multiply(stack.ipop())); break; case InstructionConstants.OP_LMUL: stack.push(stack.lpop().multiply(stack.lpop())); break; case InstructionConstants.OP_FMUL: stack.push(stack.fpop().multiply(stack.fpop())); break; case InstructionConstants.OP_DMUL: stack.push(stack.dpop().multiply(stack.dpop())); break; case InstructionConstants.OP_IDIV: try { stack.push(stack.ipop().divideOf(stack.ipop())); } catch (ArithmeticException ex) { stack.push(valueFactory.createIntegerValue()); // TODO: Forward ArithmeticExceptions. //stack.clear(); //stack.push(valueFactory.createReference(false)); //branchUnit.throwException(); } break; case InstructionConstants.OP_LDIV: try { stack.push(stack.lpop().divideOf(stack.lpop())); } catch (ArithmeticException ex) { stack.push(valueFactory.createLongValue()); // TODO: Forward ArithmeticExceptions. //stack.clear(); //stack.push(valueFactory.createReference(false)); //branchUnit.throwException(); } break; case InstructionConstants.OP_FDIV: stack.push(stack.fpop().divideOf(stack.fpop())); break; case InstructionConstants.OP_DDIV: stack.push(stack.dpop().divideOf(stack.dpop())); break; case InstructionConstants.OP_IREM: try { stack.push(stack.ipop().remainderOf(stack.ipop())); } catch (ArithmeticException ex) { stack.push(valueFactory.createIntegerValue()); // TODO: Forward ArithmeticExceptions. //stack.clear(); //stack.push(valueFactory.createReference(false)); //branchUnit.throwException(); } break; case InstructionConstants.OP_LREM: try { stack.push(stack.lpop().remainderOf(stack.lpop())); } catch (ArithmeticException ex) { stack.push(valueFactory.createLongValue()); // TODO: Forward ArithmeticExceptions. //stack.clear(); //stack.push(valueFactory.createReference(false)); //branchUnit.throwException(); } break; case InstructionConstants.OP_FREM: stack.push(stack.fpop().remainderOf(stack.fpop())); break; case InstructionConstants.OP_DREM: stack.push(stack.dpop().remainderOf(stack.dpop())); break; case InstructionConstants.OP_INEG: stack.push(stack.ipop().negate()); break; case InstructionConstants.OP_LNEG: stack.push(stack.lpop().negate()); break; case InstructionConstants.OP_FNEG: stack.push(stack.fpop().negate()); break; case InstructionConstants.OP_DNEG: stack.push(stack.dpop().negate()); break; case InstructionConstants.OP_ISHL: stack.push(stack.ipop().shiftLeftOf(stack.ipop())); break; case InstructionConstants.OP_LSHL: stack.push(stack.ipop().shiftLeftOf(stack.lpop())); break; case InstructionConstants.OP_ISHR: stack.push(stack.ipop().shiftRightOf(stack.ipop())); break; case InstructionConstants.OP_LSHR: stack.push(stack.ipop().shiftRightOf(stack.lpop())); break; case InstructionConstants.OP_IUSHR: stack.push(stack.ipop().unsignedShiftRightOf(stack.ipop())); break; case InstructionConstants.OP_LUSHR: stack.push(stack.ipop().unsignedShiftRightOf(stack.lpop())); break; case InstructionConstants.OP_IAND: stack.push(stack.ipop().and(stack.ipop())); break; case InstructionConstants.OP_LAND: stack.push(stack.lpop().and(stack.lpop())); break; case InstructionConstants.OP_IOR: stack.push(stack.ipop().or(stack.ipop())); break; case InstructionConstants.OP_LOR: stack.push(stack.lpop().or(stack.lpop())); break; case InstructionConstants.OP_IXOR: stack.push(stack.ipop().xor(stack.ipop())); break; case InstructionConstants.OP_LXOR: stack.push(stack.lpop().xor(stack.lpop())); break; case InstructionConstants.OP_I2L: stack.push(stack.ipop().convertToLong()); break; case InstructionConstants.OP_I2F: stack.push(stack.ipop().convertToFloat()); break; case InstructionConstants.OP_I2D: stack.push(stack.ipop().convertToDouble()); break; case InstructionConstants.OP_L2I: stack.push(stack.lpop().convertToInteger()); break; case InstructionConstants.OP_L2F: stack.push(stack.lpop().convertToFloat()); break; case InstructionConstants.OP_L2D: stack.push(stack.lpop().convertToDouble()); break; case InstructionConstants.OP_F2I: stack.push(stack.fpop().convertToInteger()); break; case InstructionConstants.OP_F2L: stack.push(stack.fpop().convertToLong()); break; case InstructionConstants.OP_F2D: stack.push(stack.fpop().convertToDouble()); break; case InstructionConstants.OP_D2I: stack.push(stack.dpop().convertToInteger()); break; case InstructionConstants.OP_D2L: stack.push(stack.dpop().convertToLong()); break; case InstructionConstants.OP_D2F: stack.push(stack.dpop().convertToFloat()); break; case InstructionConstants.OP_I2B: stack.push(stack.ipop().convertToByte()); break; case InstructionConstants.OP_I2C: stack.push(stack.ipop().convertToCharacter()); break; case InstructionConstants.OP_I2S: stack.push(stack.ipop().convertToShort()); break; case InstructionConstants.OP_LCMP: // stack.push(stack.lpop().compareReverse(stack.lpop())); LongValue longValue1 = stack.lpop(); LongValue longValue2 = stack.lpop(); stack.push(longValue2.compare(longValue1)); break; case InstructionConstants.OP_FCMPL: FloatValue floatValue1 = stack.fpop(); FloatValue floatValue2 = stack.fpop(); stack.push(floatValue2.compare(floatValue1)); break; case InstructionConstants.OP_FCMPG: stack.push(stack.fpop().compareReverse(stack.fpop())); break; case InstructionConstants.OP_DCMPL: DoubleValue doubleValue1 = stack.dpop(); DoubleValue doubleValue2 = stack.dpop(); stack.push(doubleValue2.compare(doubleValue1)); break; case InstructionConstants.OP_DCMPG: stack.push(stack.dpop().compareReverse(stack.dpop())); break; case InstructionConstants.OP_IRETURN: invocationUnit.exitMethod(clazz, method, stack.ipop()); branchUnit.returnFromMethod(); break; case InstructionConstants.OP_LRETURN: invocationUnit.exitMethod(clazz, method, stack.lpop()); branchUnit.returnFromMethod(); break; case InstructionConstants.OP_FRETURN: invocationUnit.exitMethod(clazz, method, stack.fpop()); branchUnit.returnFromMethod(); break; case InstructionConstants.OP_DRETURN: invocationUnit.exitMethod(clazz, method, stack.dpop()); branchUnit.returnFromMethod(); break; case InstructionConstants.OP_ARETURN: invocationUnit.exitMethod(clazz, method, stack.apop()); branchUnit.returnFromMethod(); break; case InstructionConstants.OP_RETURN: branchUnit.returnFromMethod(); break; case InstructionConstants.OP_NEWARRAY: IntegerValue arrayLength = stack.ipop(); stack.push(valueFactory.createArrayReferenceValue(String.valueOf(InstructionUtil.internalTypeFromArrayType((byte)simpleInstruction.constant)), null, arrayLength)); break; case InstructionConstants.OP_ARRAYLENGTH: ReferenceValue referenceValue = stack.apop(); stack.push(referenceValue.arrayLength(valueFactory)); break; case InstructionConstants.OP_ATHROW: ReferenceValue exceptionReferenceValue = stack.apop(); stack.clear(); stack.push(exceptionReferenceValue); branchUnit.throwException(); break; case InstructionConstants.OP_MONITORENTER: case InstructionConstants.OP_MONITOREXIT: stack.apop(); break; default: throw new IllegalArgumentException("Unknown simple instruction ["+simpleInstruction.opcode+"]"); } } public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) { int constantIndex = constantInstruction.constantIndex; switch (constantInstruction.opcode) { case InstructionConstants.OP_LDC: case InstructionConstants.OP_LDC_W: case InstructionConstants.OP_LDC2_W: stack.push(classConstantValueFactory.constantValue(clazz, constantIndex)); break; case InstructionConstants.OP_GETSTATIC: case InstructionConstants.OP_PUTSTATIC: case InstructionConstants.OP_GETFIELD: case InstructionConstants.OP_PUTFIELD: case InstructionConstants.OP_INVOKEVIRTUAL: case InstructionConstants.OP_INVOKESPECIAL: case InstructionConstants.OP_INVOKESTATIC: case InstructionConstants.OP_INVOKEINTERFACE: case InstructionConstants.OP_INVOKEDYNAMIC: invocationUnit.invokeMember(clazz, method, codeAttribute, offset, constantInstruction, stack); break; case InstructionConstants.OP_NEW: stack.push(constantValueFactory.constantValue(clazz, constantIndex).referenceValue()); break; case InstructionConstants.OP_ANEWARRAY: { ReferenceValue referenceValue = constantValueFactory.constantValue(clazz, constantIndex).referenceValue(); stack.push(valueFactory.createArrayReferenceValue(referenceValue.internalType(), referenceValue.getReferencedClass(), stack.ipop())); break; } case InstructionConstants.OP_CHECKCAST: // TODO: Check cast. ReferenceValue castValue = stack.apop(); ReferenceValue castResultValue = !alwaysCast && castValue.isNull() == Value.ALWAYS ? castValue : castValue.isNull() == Value.NEVER ? constantValueFactory.constantValue(clazz, constantIndex).referenceValue() : constantValueFactory.constantValue(clazz, constantIndex).referenceValue().generalize(valueFactory.createReferenceValueNull()); stack.push(castResultValue); break; case InstructionConstants.OP_INSTANCEOF: { ReferenceValue referenceValue = constantValueFactory.constantValue(clazz, constantIndex).referenceValue(); int instanceOf = stack.apop().instanceOf(referenceValue.getType(), referenceValue.getReferencedClass()); stack.push(instanceOf == Value.NEVER ? valueFactory.createIntegerValue(0) : instanceOf == Value.ALWAYS ? valueFactory.createIntegerValue(1) : valueFactory.createIntegerValue()); break; } case InstructionConstants.OP_MULTIANEWARRAY: { int dimensionCount = constantInstruction.constant; for (int dimension = 0; dimension < dimensionCount; dimension++) { // TODO: Use array lengths. IntegerValue arrayLength = stack.ipop(); } stack.push(constantValueFactory.constantValue(clazz, constantIndex).referenceValue()); break; } default: throw new IllegalArgumentException("Unknown constant pool instruction ["+constantInstruction.opcode+"]"); } } public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) { int variableIndex = variableInstruction.variableIndex; switch (variableInstruction.opcode) { case InstructionConstants.OP_ILOAD: case InstructionConstants.OP_ILOAD_0: case InstructionConstants.OP_ILOAD_1: case InstructionConstants.OP_ILOAD_2: case InstructionConstants.OP_ILOAD_3: stack.push(variables.iload(variableIndex)); break; case InstructionConstants.OP_LLOAD: case InstructionConstants.OP_LLOAD_0: case InstructionConstants.OP_LLOAD_1: case InstructionConstants.OP_LLOAD_2: case InstructionConstants.OP_LLOAD_3: stack.push(variables.lload(variableIndex)); break; case InstructionConstants.OP_FLOAD: case InstructionConstants.OP_FLOAD_0: case InstructionConstants.OP_FLOAD_1: case InstructionConstants.OP_FLOAD_2: case InstructionConstants.OP_FLOAD_3: stack.push(variables.fload(variableIndex)); break; case InstructionConstants.OP_DLOAD: case InstructionConstants.OP_DLOAD_0: case InstructionConstants.OP_DLOAD_1: case InstructionConstants.OP_DLOAD_2: case InstructionConstants.OP_DLOAD_3: stack.push(variables.dload(variableIndex)); break; case InstructionConstants.OP_ALOAD: case InstructionConstants.OP_ALOAD_0: case InstructionConstants.OP_ALOAD_1: case InstructionConstants.OP_ALOAD_2: case InstructionConstants.OP_ALOAD_3: stack.push(variables.aload(variableIndex)); break; case InstructionConstants.OP_ISTORE: case InstructionConstants.OP_ISTORE_0: case InstructionConstants.OP_ISTORE_1: case InstructionConstants.OP_ISTORE_2: case InstructionConstants.OP_ISTORE_3: variables.store(variableIndex, stack.ipop()); break; case InstructionConstants.OP_LSTORE: case InstructionConstants.OP_LSTORE_0: case InstructionConstants.OP_LSTORE_1: case InstructionConstants.OP_LSTORE_2: case InstructionConstants.OP_LSTORE_3: variables.store(variableIndex, stack.lpop()); break; case InstructionConstants.OP_FSTORE: case InstructionConstants.OP_FSTORE_0: case InstructionConstants.OP_FSTORE_1: case InstructionConstants.OP_FSTORE_2: case InstructionConstants.OP_FSTORE_3: variables.store(variableIndex, stack.fpop()); break; case InstructionConstants.OP_DSTORE: case InstructionConstants.OP_DSTORE_0: case InstructionConstants.OP_DSTORE_1: case InstructionConstants.OP_DSTORE_2: case InstructionConstants.OP_DSTORE_3: variables.store(variableIndex, stack.dpop()); break; case InstructionConstants.OP_ASTORE: case InstructionConstants.OP_ASTORE_0: case InstructionConstants.OP_ASTORE_1: case InstructionConstants.OP_ASTORE_2: case InstructionConstants.OP_ASTORE_3: // The operand on the stack can be a reference or a return // address, so we'll relax the pop operation. //variables.store(variableIndex, stack.apop()); variables.store(variableIndex, stack.pop()); break; case InstructionConstants.OP_IINC: variables.store(variableIndex, variables.iload(variableIndex).add( valueFactory.createIntegerValue(variableInstruction.constant))); break; case InstructionConstants.OP_RET: // The return address should be in the last offset of the // given instruction offset variable (even though there may // be other offsets). InstructionOffsetValue instructionOffsetValue = variables.oload(variableIndex); branchUnit.branch(clazz, codeAttribute, offset, instructionOffsetValue.instructionOffset(instructionOffsetValue.instructionOffsetCount()-1)); break; default: throw new IllegalArgumentException("Unknown variable instruction ["+variableInstruction.opcode+"]"); } } public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) { int branchTarget = offset + branchInstruction.branchOffset; switch (branchInstruction.opcode) { case InstructionConstants.OP_IFEQ: branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget, stack.ipop().equal(valueFactory.createIntegerValue(0))); break; case InstructionConstants.OP_IFNE: branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget, stack.ipop().notEqual(valueFactory.createIntegerValue(0))); break; case InstructionConstants.OP_IFLT: branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget, stack.ipop().lessThan(valueFactory.createIntegerValue(0))); break; case InstructionConstants.OP_IFGE: branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget, stack.ipop().greaterThanOrEqual(valueFactory.createIntegerValue(0))); break; case InstructionConstants.OP_IFGT: branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget, stack.ipop().greaterThan(valueFactory.createIntegerValue(0))); break; case InstructionConstants.OP_IFLE: branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget, stack.ipop().lessThanOrEqual(valueFactory.createIntegerValue(0))); break; case InstructionConstants.OP_IFICMPEQ: branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget, stack.ipop().equal(stack.ipop())); break; case InstructionConstants.OP_IFICMPNE: branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget, stack.ipop().notEqual(stack.ipop())); break; case InstructionConstants.OP_IFICMPLT: // Note that the stack entries are popped in reverse order. branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget, stack.ipop().greaterThan(stack.ipop())); break; case InstructionConstants.OP_IFICMPGE: // Note that the stack entries are popped in reverse order. branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget, stack.ipop().lessThanOrEqual(stack.ipop())); break; case InstructionConstants.OP_IFICMPGT: // Note that the stack entries are popped in reverse order. branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget, stack.ipop().lessThan(stack.ipop())); break; case InstructionConstants.OP_IFICMPLE: // Note that the stack entries are popped in reverse order. branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget, stack.ipop().greaterThanOrEqual(stack.ipop())); break; case InstructionConstants.OP_IFACMPEQ: branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget, stack.apop().equal(stack.apop())); break; case InstructionConstants.OP_IFACMPNE: branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget, stack.apop().notEqual(stack.apop())); break; case InstructionConstants.OP_GOTO: case InstructionConstants.OP_GOTO_W: branchUnit.branch(clazz, codeAttribute, offset, branchTarget); break; case InstructionConstants.OP_JSR: case InstructionConstants.OP_JSR_W: stack.push(new InstructionOffsetValue(offset + branchInstruction.length(offset))); branchUnit.branch(clazz, codeAttribute, offset, branchTarget); break; case InstructionConstants.OP_IFNULL: branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget, stack.apop().isNull()); break; case InstructionConstants.OP_IFNONNULL: branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget, stack.apop().isNotNull()); break; default: throw new IllegalArgumentException("Unknown branch instruction ["+branchInstruction.opcode+"]"); } } public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction) { IntegerValue indexValue = stack.ipop(); // If there is no definite branch in any of the cases below, // branch to the default offset. branchUnit.branch(clazz, codeAttribute, offset, offset + tableSwitchInstruction.defaultOffset); for (int index = 0; index < tableSwitchInstruction.jumpOffsets.length; index++) { int conditional = indexValue.equal(valueFactory.createIntegerValue( tableSwitchInstruction.lowCase + index)); branchUnit.branchConditionally(clazz, codeAttribute, offset, offset + tableSwitchInstruction.jumpOffsets[index], conditional); // If this branch is always taken, we can skip the rest. if (conditional == Value.ALWAYS) { break; } } } public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) { IntegerValue indexValue = stack.ipop(); // If there is no definite branch in any of the cases below, // branch to the default offset. branchUnit.branch(clazz, codeAttribute, offset, offset + lookUpSwitchInstruction.defaultOffset); for (int index = 0; index < lookUpSwitchInstruction.jumpOffsets.length; index++) { int conditional = indexValue.equal(valueFactory.createIntegerValue( lookUpSwitchInstruction.cases[index])); branchUnit.branchConditionally(clazz, codeAttribute, offset, offset + lookUpSwitchInstruction.jumpOffsets[index], conditional); // If this branch is always taken, we can skip the rest. if (conditional == Value.ALWAYS) { break; } } } }