/** Marks the code starting at the given offset. */ private void markCode(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset) { boolean oldNext = next; byte[] code = codeAttribute.code; // Continue with the current instruction as long as we haven't marked it // yet. while (!isReachable[offset]) { // Get the current instruction. Instruction instruction = InstructionFactory.create(code, offset); // Mark it as reachable. isReachable[offset] = true; // By default, we'll assume we can continue with the next // instruction in a moment. next = true; // Mark the branch targets, if any. instruction.accept(clazz, method, codeAttribute, offset, this); // Can we really continue with the next instruction? if (!next) { break; } // Go to the next instruction. offset += instruction.length(offset); } next = oldNext; }
/** Returns whether the given instruction may throw exceptions. */ public boolean mayThrowExceptions( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) { mayThrowExceptions = false; instruction.accept(clazz, method, codeAttribute, offset, this); return mayThrowExceptions; }
/** * Evaluates a block of instructions that hasn't been handled before, starting at the given offset * and ending at a branch instruction, a return instruction, or a throw instruction. Branch * instructions are handled recursively. */ private void evaluateInstructionBlock( Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset) { if (DEBUG) { if (evaluated[instructionOffset]) { System.out.println("-- (instruction block at " + instructionOffset + " already evaluated)"); } else { System.out.println("-- instruction block:"); } } // Remember the initial stack size. int initialStackSize = stackSize; // Remember the maximum stack size. if (maxStackSize < stackSize) { maxStackSize = stackSize; } // Evaluate any instructions that haven't been evaluated before. while (!evaluated[instructionOffset]) { // Mark the instruction as evaluated. evaluated[instructionOffset] = true; Instruction instruction = InstructionFactory.create(codeAttribute.code, instructionOffset); if (DEBUG) { int stackPushCount = instruction.stackPushCount(clazz); int stackPopCount = instruction.stackPopCount(clazz); System.out.println( "[" + instructionOffset + "]: " + stackSize + " - " + stackPopCount + " + " + stackPushCount + " = " + (stackSize + stackPushCount - stackPopCount) + ": " + instruction.toString(instructionOffset)); } // Compute the instruction's effect on the stack size. stackSize -= instruction.stackPopCount(clazz); if (stackSize < 0) { throw new IllegalArgumentException( "Stack size becomes negative after instruction " + instruction.toString(instructionOffset) + " in [" + clazz.getName() + "." + method.getName(clazz) + method.getDescriptor(clazz) + "]"); } stackSizes[instructionOffset] = stackSize += instruction.stackPushCount(clazz); // Remember the maximum stack size. if (maxStackSize < stackSize) { maxStackSize = stackSize; } // Remember the next instruction offset. int nextInstructionOffset = instructionOffset + instruction.length(instructionOffset); // Visit the instruction, in order to handle branches. instruction.accept(clazz, method, codeAttribute, instructionOffset, this); // Stop evaluating after a branch. if (exitInstructionBlock) { break; } // Continue with the next instruction. instructionOffset = nextInstructionOffset; if (DEBUG) { if (evaluated[instructionOffset]) { System.out.println("-- (instruction at " + instructionOffset + " already evaluated)"); } } } // Restore the stack size for possible subsequent instruction blocks. this.stackSize = initialStackSize; }
public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) { // DEBUG = // clazz.getName().equals("abc/Def") && // method.getName(clazz).equals("abc"); if (DEBUG) { method.accept(clazz, new ClassPrinter()); } branchTargetFinder.visitCodeAttribute(clazz, method, codeAttribute); // Don't bother if there aren't any subroutines anyway. if (!containsSubroutines(codeAttribute)) { return; } if (DEBUG) { System.out.println( "SubroutineInliner: processing [" + clazz.getName() + "." + method.getName(clazz) + method.getDescriptor(clazz) + "]"); } // Append the body of the code. codeAttributeComposer.reset(); codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength); // Copy the non-subroutine instructions. int offset = 0; while (offset < codeAttribute.u4codeLength) { Instruction instruction = InstructionFactory.create(codeAttribute.code, offset); int instructionLength = instruction.length(offset); // Is this returning subroutine? if (branchTargetFinder.isSubroutine(offset) && branchTargetFinder.isSubroutineReturning(offset)) { // Skip the subroutine. if (DEBUG) { System.out.println( " Skipping original subroutine instruction " + instruction.toString(offset)); } // Append a label at this offset instead. codeAttributeComposer.appendLabel(offset); } else { // Copy the instruction, inlining any subroutine call recursively. instruction.accept(clazz, method, codeAttribute, offset, this); } offset += instructionLength; } // Copy the exceptions. Note that exceptions with empty try blocks // are automatically removed. codeAttribute.exceptionsAccept(clazz, method, subroutineExceptionInliner); if (DEBUG) { System.out.println(" Appending label after code at [" + offset + "]"); } // Append a label just after the code. codeAttributeComposer.appendLabel(codeAttribute.u4codeLength); // End and update the code attribute. codeAttributeComposer.endCodeFragment(); codeAttributeComposer.visitCodeAttribute(clazz, method, codeAttribute); if (DEBUG) { method.accept(clazz, new ClassPrinter()); } }