in src/main/java/org/apache/bcel/verifier/structurals/Pass3bVerifier.java [161:318]
private void circulationPump(final MethodGen m, final ControlFlowGraph cfg, final InstructionContext start, final Frame vanillaFrame,
final InstConstraintVisitor icv, final ExecutionVisitor ev) {
final Random random = new Random();
final InstructionContextQueue icq = new InstructionContextQueue();
start.execute(vanillaFrame, new ArrayList<>(), icv, ev);
// new ArrayList() <=> no Instruction was executed before
// => Top-Level routine (no jsr call before)
icq.add(start, new ArrayList<>());
// LOOP!
while (!icq.isEmpty()) {
final InstructionContext u;
final ArrayList<InstructionContext> ec;
if (!DEBUG) {
final int r = random.nextInt(icq.size());
u = icq.getIC(r);
ec = icq.getEC(r);
icq.remove(r);
} else {
u = icq.getIC(0);
ec = icq.getEC(0);
icq.remove(0);
}
@SuppressWarnings("unchecked") // ec is of type ArrayList<InstructionContext>
final ArrayList<InstructionContext> oldchain = (ArrayList<InstructionContext>) ec.clone();
@SuppressWarnings("unchecked") // ec is of type ArrayList<InstructionContext>
final ArrayList<InstructionContext> newchain = (ArrayList<InstructionContext>) ec.clone();
newchain.add(u);
if (u.getInstruction().getInstruction() instanceof RET) {
//System.err.println(u);
// We can only follow _one_ successor, the one after the
// JSR that was recently executed.
final RET ret = (RET) u.getInstruction().getInstruction();
final ReturnaddressType t = (ReturnaddressType) u.getOutFrame(oldchain).getLocals().get(ret.getIndex());
final InstructionContext theSuccessor = cfg.contextOf(t.getTarget());
// Sanity check
InstructionContext lastJSR = null;
int skipJsr = 0;
for (int ss = oldchain.size() - 1; ss >= 0; ss--) {
if (skipJsr < 0) {
throw new AssertionViolatedException("More RET than JSR in execution chain?!");
}
//System.err.println("+"+oldchain.get(ss));
if (oldchain.get(ss).getInstruction().getInstruction() instanceof JsrInstruction) {
if (skipJsr == 0) {
lastJSR = oldchain.get(ss);
break;
}
skipJsr--;
}
if (oldchain.get(ss).getInstruction().getInstruction() instanceof RET) {
skipJsr++;
}
}
if (lastJSR == null) {
throw new AssertionViolatedException("RET without a JSR before in ExecutionChain?! EC: '" + oldchain + "'.");
}
final JsrInstruction jsr = (JsrInstruction) lastJSR.getInstruction().getInstruction();
if (theSuccessor != cfg.contextOf(jsr.physicalSuccessor())) {
throw new AssertionViolatedException("RET '" + u.getInstruction() + "' info inconsistent: jump back to '" + theSuccessor + "' or '"
+ cfg.contextOf(jsr.physicalSuccessor()) + "'?");
}
if (theSuccessor.execute(u.getOutFrame(oldchain), newchain, icv, ev)) {
@SuppressWarnings("unchecked") // newchain is already of type ArrayList<InstructionContext>
final ArrayList<InstructionContext> newchainClone = (ArrayList<InstructionContext>) newchain.clone();
icq.add(theSuccessor, newchainClone);
}
} else { // "not a ret"
// Normal successors. Add them to the queue of successors.
final InstructionContext[] succs = u.getSuccessors();
for (final InstructionContext v : succs) {
if (v.execute(u.getOutFrame(oldchain), newchain, icv, ev)) {
@SuppressWarnings("unchecked") // newchain is already of type ArrayList<InstructionContext>
final ArrayList<InstructionContext> newchainClone = (ArrayList<InstructionContext>) newchain.clone();
icq.add(v, newchainClone);
}
}
} // end "not a ret"
// Exception Handlers. Add them to the queue of successors.
// [subroutines are never protected; mandated by JustIce]
final ExceptionHandler[] excHds = u.getExceptionHandlers();
for (final ExceptionHandler excHd : excHds) {
final InstructionContext v = cfg.contextOf(excHd.getHandlerStart());
// TODO: the "oldchain" and "newchain" is used to determine the subroutine
// we're in (by searching for the last JSR) by the InstructionContext
// implementation. Therefore, we should not use this chain mechanism
// when dealing with exception handlers.
// Example: a JSR with an exception handler as its successor does not
// mean we're in a subroutine if we go to the exception handler.
// We should address this problem later; by now we simply "cut" the chain
// by using an empty chain for the exception handlers.
// if (v.execute(new Frame(u.getOutFrame(oldchain).getLocals(),
// new OperandStack (u.getOutFrame().getStack().maxStack(),
// (exc_hds[s].getExceptionType() == null ? Type.THROWABLE : exc_hds[s].getExceptionType())) ), newchain), icv, ev) {
// icq.add(v, (ArrayList) newchain.clone());
if (v.execute(new Frame(u.getOutFrame(oldchain).getLocals(), new OperandStack(u.getOutFrame(oldchain).getStack().maxStack(),
excHd.getExceptionType() == null ? Type.THROWABLE : excHd.getExceptionType())), new ArrayList<>(), icv, ev)) {
icq.add(v, new ArrayList<>());
}
}
} // while (!icq.isEmpty()) END
InstructionHandle ih = start.getInstruction();
do {
if (ih.getInstruction() instanceof ReturnInstruction && !cfg.isDead(ih)) {
final InstructionContext ic = cfg.contextOf(ih);
// TODO: This is buggy, we check only the top-level return instructions this way.
// Maybe some maniac returns from a method when in a subroutine?
final Frame f = ic.getOutFrame(new ArrayList<>());
final LocalVariables lvs = f.getLocals();
for (int i = 0; i < lvs.maxLocals(); i++) {
if (lvs.get(i) instanceof UninitializedObjectType) {
addMessage("Warning: ReturnInstruction '" + ic + "' may leave method with an uninitialized object in the local variables array '"
+ lvs + "'.");
}
}
final OperandStack os = f.getStack();
for (int i = 0; i < os.size(); i++) {
if (os.peek(i) instanceof UninitializedObjectType) {
addMessage(
"Warning: ReturnInstruction '" + ic + "' may leave method with an uninitialized object on the operand stack '" + os + "'.");
}
}
// see JVM $4.8.2
Type returnedType = null;
final OperandStack inStack = ic.getInFrame().getStack();
if (inStack.size() >= 1) {
returnedType = inStack.peek();
} else {
returnedType = Type.VOID;
}
if (returnedType != null) {
if (returnedType instanceof ReferenceType) {
try {
if (!((ReferenceType) returnedType).isCastableTo(m.getReturnType())) {
invalidReturnTypeError(returnedType, m);
}
} catch (final ClassNotFoundException e) {
// Don't know what to do now, so raise RuntimeException
throw new IllegalArgumentException(e);
}
} else if (!returnedType.equals(m.getReturnType().normalizeForStackOrLocal())) {
invalidReturnTypeError(returnedType, m);
}
}
}
} while ((ih = ih.getNext()) != null);
}