private void circulationPump()

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);

    }