// // 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.ClassLoaderInfo; import gov.nasa.jpf.vm.ElementInfo; import gov.nasa.jpf.vm.Instruction; import gov.nasa.jpf.vm.LoadOnJPFRequired; import gov.nasa.jpf.vm.MethodInfo; import gov.nasa.jpf.vm.ThreadInfo; /** * Invoke instance method; special handling for superclass, private, * and instance initialization method invocations * ..., objectref, [arg1, [arg2 ...]] => ... * * invokedMethod is supposed to be constant (ClassInfo can't change) */ public class INVOKESPECIAL extends InstanceInvocation { public INVOKESPECIAL (String clsDescriptor, String methodName, String signature){ super(clsDescriptor, methodName, signature); } public int getByteCode () { return 0xB7; } public Conditional<Instruction> execute (FeatureExpr ctx, ThreadInfo ti) { int argSize = getArgSize(); Conditional<Integer> allRefs = ti.getCalleeThis( ctx, argSize); 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); String methName = 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()) { if (splitRef) { ctx = ctx.and(objRefEntry.getValue()); } Integer objRef = objRefEntry.getKey(); lastObj = objRef; // we don't have to check for NULL objects since this is either a ctor, a // private method, or a super method MethodInfo callee; try { callee = getInvokedMethod(ctx, ti); } catch(LoadOnJPFRequired rre) { return ti.getPC(); } 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){ return new One<>(ti.createAndThrowException(ctx, "java.lang.NoSuchMethodException", "Calling " + cname + '.' + mname)); } ElementInfo ei = ti.getElementInfoWithUpdatedSharedness(objRef); if (callee.isSynchronized()){ if (checkSyncCG(ei, ti)){ return new One<Instruction>(this); } } // if (!classes.isEmpty() && map.size() > classes.size()) { // System.out.println("SPECIAL reduce invocations from " + map.size() + " to " + classes.size() + " " + callee); // } setupCallee(ctx, ti, callee); // this creates, initializes and // pushes the callee StackFrame 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; } /** * we can do some more caching here - the MethodInfo should be const */ public MethodInfo getInvokedMethod (FeatureExpr ctx, ThreadInfo th) { // since INVOKESPECIAL is only used for private methods and ctors, // we don't have to deal with null object calls if (invokedMethod == null) { ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(cname); boolean recursiveLookup = (mname.charAt(0) != '<'); // no hierarchy lookup for <init> invokedMethod = ci.getMethod(mname, recursiveLookup); } return invokedMethod; // we can store internally } public String toString() { return (super.toString() + " " + cname + '.' + mname); } @Override 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); } }