/** * Whenever the outgoing frame situation of an InstructionContext changes, all its successors are * put [back] into the queue [as if they were unvisited]. The proof of termination is about the * existence of a fix point of frame merging. */ private void circulationPump( ControlFlowGraph cfg, InstructionContext start, Frame vanillaFrame, InstConstraintVisitor icv, ExecutionVisitor ev) { final Random random = new Random(); 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()) { InstructionContext u; ArrayList ec; if (!DEBUG) { 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); } ArrayList oldchain = (ArrayList) (ec.clone()); ArrayList newchain = (ArrayList) (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. RET ret = (RET) (u.getInstruction().getInstruction()); ReturnaddressType t = (ReturnaddressType) u.getOutFrame(oldchain).getLocals().get(ret.getIndex()); InstructionContext theSuccessor = cfg.contextOf(t.getTarget()); // Sanity check InstructionContext lastJSR = null; int skip_jsr = 0; for (int ss = oldchain.size() - 1; ss >= 0; ss--) { if (skip_jsr < 0) { throw new AssertionViolatedException("More RET than JSR in execution chain?!"); } // System.err.println("+"+oldchain.get(ss)); if (((InstructionContext) oldchain.get(ss)).getInstruction().getInstruction() instanceof JsrInstruction) { if (skip_jsr == 0) { lastJSR = (InstructionContext) oldchain.get(ss); break; } else { skip_jsr--; } } if (((InstructionContext) oldchain.get(ss)).getInstruction().getInstruction() instanceof RET) { skip_jsr++; } } if (lastJSR == null) { throw new AssertionViolatedException( "RET without a JSR before in ExecutionChain?! EC: '" + oldchain + "'."); } 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)) { icq.add(theSuccessor, (ArrayList) newchain.clone()); } } else { // "not a ret" // Normal successors. Add them to the queue of successors. InstructionContext[] succs = u.getSuccessors(); for (int s = 0; s < succs.length; s++) { InstructionContext v = succs[s]; if (v.execute(u.getOutFrame(oldchain), newchain, icv, ev)) { icq.add(v, (ArrayList) newchain.clone()); } } } // end "not a ret" // Exception Handlers. Add them to the queue of successors. // [subroutines are never protected; mandated by JustIce] ExceptionHandler[] exc_hds = u.getExceptionHandlers(); for (int s = 0; s < exc_hds.length; s++) { InstructionContext v = cfg.contextOf(exc_hds[s].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(), (exc_hds[s].getExceptionType() == null ? Type.THROWABLE : exc_hds[s].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)))) { InstructionContext ic = cfg.contextOf(ih); Frame f = ic.getOutFrame( new ArrayList()); // 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? LocalVariables lvs = f.getLocals(); for (int i = 0; i < lvs.maxLocals(); i++) { if (lvs.get(i) instanceof UninitializedObjectType) { this.addMessage( "Warning: ReturnInstruction '" + ic + "' may leave method with an uninitialized object in the local variables array '" + lvs + "'."); } } OperandStack os = f.getStack(); for (int i = 0; i < os.size(); i++) { if (os.peek(i) instanceof UninitializedObjectType) { this.addMessage( "Warning: ReturnInstruction '" + ic + "' may leave method with an uninitialized object on the operand stack '" + os + "'."); } } } } while ((ih = ih.getNext()) != null); }
@Test public void TopValueRemovedOnPop() { values.replaceTop(new BigDecimal(25)); values.pop(); assertEquals(BigDecimal.ZERO, values.peek()); }
@Test public void CanHaveValuesPushed() { values.push(BigDecimal.ONE); assertEquals(BigDecimal.ONE, values.peek()); }
@Test public void CanReplaceTop() { values.replaceTop(BigDecimal.TEN); assertEquals(BigDecimal.TEN, values.peek()); }
@Test public void IsNeverEmpty() { assertEquals(BigDecimal.ZERO, values.peek()); }
/** * Whenever the outgoing frame situation of an InstructionContext changes, all its successors are * put [back] into the queue [as if they were unvisited]. The proof of termination is about the * existence of a fix point of frame merging. */ private void circulationPump( MethodGen m, ControlFlowGraph cfg, InstructionContext start, Frame vanillaFrame, InstConstraintVisitor icv, ExecutionVisitor ev) { final Random random = new Random(); InstructionContextQueue icq = new InstructionContextQueue(); start.execute( vanillaFrame, new ArrayList<InstructionContext>(), icv, ev); // new ArrayList() <=> no Instruction was executed before // => Top-Level routine (no jsr call before) icq.add(start, new ArrayList<InstructionContext>()); // LOOP! while (!icq.isEmpty()) { InstructionContext u; ArrayList<InstructionContext> ec; if (!DEBUG) { 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> ArrayList<InstructionContext> oldchain = (ArrayList<InstructionContext>) (ec.clone()); @SuppressWarnings("unchecked") // ec is of type ArrayList<InstructionContext> 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. RET ret = (RET) (u.getInstruction().getInstruction()); ReturnaddressType t = (ReturnaddressType) u.getOutFrame(oldchain).getLocals().get(ret.getIndex()); InstructionContext theSuccessor = cfg.contextOf(t.getTarget()); // Sanity check InstructionContext lastJSR = null; int skip_jsr = 0; for (int ss = oldchain.size() - 1; ss >= 0; ss--) { if (skip_jsr < 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 (skip_jsr == 0) { lastJSR = oldchain.get(ss); break; } skip_jsr--; } if ((oldchain.get(ss)).getInstruction().getInstruction() instanceof RET) { skip_jsr++; } } if (lastJSR == null) { throw new AssertionViolatedException( "RET without a JSR before in ExecutionChain?! EC: '" + oldchain + "'."); } 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> ArrayList<InstructionContext> newchainClone = (ArrayList<InstructionContext>) newchain.clone(); icq.add(theSuccessor, newchainClone); } } else { // "not a ret" // Normal successors. Add them to the queue of successors. InstructionContext[] succs = u.getSuccessors(); for (InstructionContext v : succs) { if (v.execute(u.getOutFrame(oldchain), newchain, icv, ev)) { @SuppressWarnings( "unchecked") // newchain is already of type ArrayList<InstructionContext> 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] ExceptionHandler[] exc_hds = u.getExceptionHandlers(); for (ExceptionHandler exc_hd : exc_hds) { InstructionContext v = cfg.contextOf(exc_hd.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(), (exc_hd.getExceptionType() == null ? Type.THROWABLE : exc_hd.getExceptionType()))), new ArrayList<InstructionContext>(), icv, ev)) { icq.add(v, new ArrayList<InstructionContext>()); } } } // while (!icq.isEmpty()) END InstructionHandle ih = start.getInstruction(); do { if ((ih.getInstruction() instanceof ReturnInstruction) && (!(cfg.isDead(ih)))) { InstructionContext ic = cfg.contextOf(ih); Frame f = ic.getOutFrame( new ArrayList< InstructionContext>()); // 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? LocalVariables lvs = f.getLocals(); for (int i = 0; i < lvs.maxLocals(); i++) { if (lvs.get(i) instanceof UninitializedObjectType) { this.addMessage( "Warning: ReturnInstruction '" + ic + "' may leave method with an uninitialized object in the local variables array '" + lvs + "'."); } } OperandStack os = f.getStack(); for (int i = 0; i < os.size(); i++) { if (os.peek(i) instanceof UninitializedObjectType) { this.addMessage( "Warning: ReturnInstruction '" + ic + "' may leave method with an uninitialized object on the operand stack '" + os + "'."); } } // see JVM $4.8.2 // TODO implement all based on stack Type returnedType = null; InstructionHandle ihPrev = null; ihPrev = ih.getPrev(); if (ihPrev != null) { if (ihPrev.getInstruction() instanceof InvokeInstruction) { returnedType = ((InvokeInstruction) ihPrev.getInstruction()).getType(m.getConstantPool()); } if (ihPrev.getInstruction() instanceof LoadInstruction) { int index = ((LoadInstruction) ihPrev.getInstruction()).getIndex(); returnedType = lvs.get(index); } if (ihPrev.getInstruction() instanceof GETFIELD) { returnedType = ((GETFIELD) ihPrev.getInstruction()).getType(m.getConstantPool()); } } if (returnedType != null) { if (returnedType instanceof ObjectType) { try { if (!((ObjectType) returnedType).isAssignmentCompatibleWith(m.getReturnType())) { throw new StructuralCodeConstraintException( "Returned type " + returnedType + " does not match Method's return type " + m.getReturnType()); } } catch (ClassNotFoundException e) { // dont know what do do now, so raise RuntimeException throw new RuntimeException(e); } } else if (!returnedType.equals(m.getReturnType())) { throw new StructuralCodeConstraintException( "Returned type " + returnedType + " does not match Method's return type " + m.getReturnType()); } } } } while ((ih = ih.getNext()) != null); }