// // Copyright (C) 2006 United States Government as represented by the // Administrator of the National Aeronautics and Space Administration // (NASA). All Rights Reserved. // // This software is distributed under the NASA Open Source Agreement // (NOSA), version 1.3. The NOSA has been approved by the Open Source // Initiative. See the file NOSA-1.3-JPF at the top of the distribution // directory tree for the complete NOSA document. // // THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY // KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT // LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO // SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR // A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT // THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT // DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE. // package gov.nasa.jpf.jvm.bytecode; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import cmu.conditional.ChoiceFactory; import cmu.conditional.Conditional; import cmu.conditional.One; import de.fosd.typechef.featureexpr.FeatureExpr; import de.fosd.typechef.featureexpr.FeatureExprFactory; import gov.nasa.jpf.JPF; import gov.nasa.jpf.vm.ClassInfo; import gov.nasa.jpf.vm.ElementInfo; import gov.nasa.jpf.vm.Instruction; import gov.nasa.jpf.vm.MJIEnv; import gov.nasa.jpf.vm.MethodInfo; import gov.nasa.jpf.vm.ThreadInfo; /** * a base class for virtual call instructions */ public abstract class VirtualInvocation extends InstanceInvocation { // note that we can't null laseCalleeCi and invokedMethod in // cleanupTransients() // since we use it as an internal optimization (loops with repeated calls on // the // same object) ClassInfo lastCalleeCi; // cached for performance protected VirtualInvocation() { } protected VirtualInvocation(String clsDescriptor, String methodName, String signature) { super(clsDescriptor, methodName, signature); } public Conditional<Instruction> execute(FeatureExpr ctx, final ThreadInfo ti) { Conditional<Integer> allRefs = ti.getCalleeThis(ctx, getArgSize()); Map<Integer, FeatureExpr> map = allRefs.toMap(); Map<String, List<FeatureExpr>> classes = new TreeMap<>(); if (JPF.SHARE_INVOCATIONS && map.size() > 1) { for (Entry<Integer, FeatureExpr> e : map.entrySet()) { MethodInfo callee = getInvokedMethod(ctx.and(e.getValue()), ti, e.getKey()); String methName = callee == null ? "" : callee.getFullName(); if (classes.containsKey(methName)) { classes.get(methName).add(e.getValue()); } else { List<FeatureExpr> list = new ArrayList<>(map.size()); list.add(e.getValue()); classes.put(methName, list); } } } boolean splitRef = false; if ((JPF.SHARE_INVOCATIONS && classes.size() > 1) || (!JPF.SHARE_INVOCATIONS && map.size() > 1)) { splitRef = true; } for (Entry<Integer, FeatureExpr> objRefEntry : map.entrySet()) { Integer objRef = objRefEntry.getKey(); if (objRef == MJIEnv.NULL) { lastObj = MJIEnv.NULL; return ChoiceFactory.create(ctx.and(objRefEntry.getValue()), new One<Instruction>(new EXCEPTION(this, java.lang.NullPointerException.class.getName(), "Calling '" + mname + "' on null object")), ChoiceFactory.create(ctx, new One<>(typeSafeClone(mi)), ti.getPC())).simplify(); } MethodInfo callee = getInvokedMethod(ctx.and(objRefEntry.getValue()), ti, objRef); ElementInfo ei = ti.getElementInfoWithUpdatedSharedness(objRef); if (!classes.isEmpty()) { FeatureExpr invocationCtx = FeatureExprFactory.False(); for (FeatureExpr e : classes.get(callee.getFullName())) { invocationCtx = invocationCtx.or(e); } ctx = ctx.and(invocationCtx); } else { ctx = ctx.and(objRefEntry.getValue()); } if (callee == null) { String clsName = ti.getClassInfo(objRef).getName(); return new One<>(ti.createAndThrowException(ctx, java.lang.NoSuchMethodError.class.getName(), clsName + '.' + mname)); } else { if (callee.isAbstract()) { return new One<>(ti.createAndThrowException(ctx, java.lang.AbstractMethodError.class.getName(), callee.getFullName() + ", object: " + ei)); } } if (callee.isSynchronized()) { if (checkSyncCG(ei, ti)) { return new One<>(this); } } setupCallee(ctx, ti, callee); if (!splitRef) { return ti.getPC(); } return ChoiceFactory.create(ctx, ti.getPC(), new One<>(typeSafeClone(mi))).simplify(); } throw new RuntimeException("Something went wrong"); } /** * If the current thread already owns the lock, then the current thread can * go on. For example, this is a recursive acquisition. */ protected boolean isLockOwner(ThreadInfo ti, ElementInfo ei) { return ei.getLockingThread() == ti; } /** * If the object will still be owned, then the current thread can go on. For * example, all but the last monitorexit for the object. */ protected boolean isLastUnlock(ElementInfo ei) { return ei.getLockCount() == 1; } public MethodInfo getInvokedMethod(FeatureExpr ctx, ThreadInfo ti) { int objRef; if (ti.getNextPC() == null) { // this is pre-exec objRef = ti.getCalleeThis(FeatureExprFactory.True(), getArgSize()).getValue(); } else { // this is post-exec objRef = lastObj; } return getInvokedMethod(ctx, ti, objRef); } public MethodInfo getInvokedMethod(FeatureExpr ctx, ThreadInfo ti, int objRef) { if (objRef != MJIEnv.NULL) { lastObj = objRef; ClassInfo cci = ti.getClassInfo(objRef); if (lastCalleeCi != cci) { // callee ClassInfo has changed lastCalleeCi = cci; if (cci == null) { System.out.println("Class not found for " + mname); ti.createAndThrowException(ctx, ClassNotFoundException.class.getName(), mname); return null; } invokedMethod = cci.getMethod(mname, true); if (invokedMethod == null) { invokedMethod = cci.getDefaultMethod(mname); // here we could catch the NoSuchMethodError if (invokedMethod == null) { lastObj = MJIEnv.NULL; lastCalleeCi = null; } } } } else { lastObj = MJIEnv.NULL; lastCalleeCi = null; invokedMethod = null; } return invokedMethod; } public Object getFieldValue(String id, ThreadInfo ti) { int objRef = getCalleeThis(ti); ElementInfo ei = ti.getElementInfo(objRef); Object v = ei.getFieldValueObject(id); if (v == null) { // try a static field v = ei.getClassInfo().getStaticFieldValueObject(id); } return v; } public void accept(InstructionVisitor insVisitor) { insVisitor.visit(this); } @Override public Instruction typeSafeClone(MethodInfo clonedMethod) { VirtualInvocation clone = null; try { clone = (VirtualInvocation) super.clone(); // reset the method that this insn belongs to clone.mi = clonedMethod; clone.lastCalleeCi = null; clone.invokedMethod = null; } catch (CloneNotSupportedException e) { e.printStackTrace(); } return clone; } }