// // 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 de.fosd.typechef.featureexpr.FeatureExpr; import de.fosd.typechef.featureexpr.FeatureExprFactory; import gov.nasa.jpf.jvm.JVMInstruction; import gov.nasa.jpf.vm.ChoiceGenerator; import gov.nasa.jpf.vm.ClassInfo; import gov.nasa.jpf.vm.ElementInfo; import gov.nasa.jpf.vm.Instruction; import gov.nasa.jpf.vm.LocalVarInfo; import gov.nasa.jpf.vm.MJIEnv; import gov.nasa.jpf.vm.MethodInfo; import gov.nasa.jpf.vm.StackFrame; import gov.nasa.jpf.vm.ThreadInfo; import gov.nasa.jpf.vm.Types; import gov.nasa.jpf.vm.VM; /** * abstraction for all invoke instructions */ public abstract class InvokeInstruction extends JVMInstruction { /* Those are all from the BCEL class, i.e. straight from the class file. * Note that we can't directly resolve to MethodInfo objects because * the corresponding class might not be loaded yet (has to be done * on execution) */ protected String cname; protected String mname; protected String signature; protected int argSize = -1; /** to cache the last callee object */ protected int lastObj = Integer.MIN_VALUE; /** * watch out - this is only const for static and special invocation * all virtuals will use it only as a cache */ protected MethodInfo invokedMethod; protected Object[] arguments; // temporary cache for arg values (for listeners) protected InvokeInstruction (String clsName, String methodName, String signature){ this.cname = Types.getClassNameFromTypeName(clsName); this.signature = signature; this.mname = MethodInfo.getUniqueName(methodName, signature); } protected InvokeInstruction () {} public int getLength() { return 3; // opcode, index1, index2 } // only useful from post-exec notifications public int getLastObjRef() { return lastObj; } /** * this is for explicit initialization (not BCEL) */ public void setInvokedMethod (String clsName, String mthName, String sig) { cname = clsName; mname = mthName + sig; signature = sig; } /** * be aware of that this might differ from getInvokedMethod(), since it only * denotes the target type info we have at the static point of the call, i.e. * before dynamic dispatching */ public String getInvokedMethodClassName() { return cname; } public String getInvokedMethodSignature() { return signature; } public String getInvokedMethodName () { return mname; } public abstract MethodInfo getInvokedMethod (FeatureExpr ctx, ThreadInfo ti); public MethodInfo getInvokedMethod () { if (invokedMethod == null){ invokedMethod = getInvokedMethod(FeatureExprFactory.True(), ThreadInfo.getCurrentThread()); } return invokedMethod; } public boolean isCompleted(ThreadInfo ti) { Instruction nextPc = ti.getNextPC().getValue(); if (nextPc == null || nextPc == this){ return false; } if (invokedMethod != null){ MethodInfo topMethod = ti.getTopFrame().getMethodInfo(); if (invokedMethod.isMJI() && (topMethod == mi)) { // same frame -> this was a native method that already returned return true; } if (topMethod == invokedMethod){ return true; } } // <2do> how do we account for exceptions? return false; } StackFrame getCallerFrame (ThreadInfo ti, MethodInfo callee) { return ti.getStackFrameExecuting(this, 0); } //--- invocation processing protected void setupCallee (FeatureExpr ctx, ThreadInfo ti, MethodInfo callee){ ClassInfo ciCaller = callee.getClassInfo(); StackFrame frame = ciCaller.createStackFrame(ctx, ti, callee); ti.pushFrame(frame); ti.enter(); } /** * this is a little helper to find out about call argument values from listeners that * don't want to dig through MethodInfos and Types. Reference arguments are returned as * either ElementInfos or 'null', all others are boxed (i.e. a 'double' is returned as * a 'Double' object). * It goes without saying that this method can only be called during an executeInstruction() * or instructionExecuted() notification for the corresponding InvokeInstruction * We use the caller frame to retrieve the arguments (instead of the locals of * the callee) since that works for both pre- and post-exec notification */ public Object[] getArgumentValues (ThreadInfo ti) { MethodInfo callee = getInvokedMethod(FeatureExprFactory.True(), ti); StackFrame frame = getCallerFrame(ti, callee); assert frame != null : "can't find caller stackframe for: " + this; return getArgsFromCaller(ti, frame, callee); } public Object[] getArgumentAttrs (ThreadInfo ti) { MethodInfo callee = getInvokedMethod(FeatureExprFactory.True(), ti); StackFrame frame = getCallerFrame(ti, callee); assert frame != null : "can't find caller stackframe for: " + this; return frame.getArgumentAttrs(callee); } /** * check if there is any argument attr of the specified type * (use this before using any of the more expensive retrievers) */ public boolean hasArgumentAttr (ThreadInfo ti, Class<?> type){ MethodInfo callee = getInvokedMethod(FeatureExprFactory.True(), ti); StackFrame frame = getCallerFrame(ti, callee); assert frame != null : "can't find caller stackframe for: " + this; return frame.hasArgumentAttr(callee,type); } /** * do we have a reference argument that has an object attribute? * less efficient, but still without object creation */ public boolean hasAttrRefArgument (ThreadInfo ti, Class<?> type){ MethodInfo callee = getInvokedMethod(FeatureExprFactory.True(), ti); StackFrame frame = getCallerFrame(ti, callee); int nArgSlots = callee.getArgumentsSize(); for (int i=0; i<nArgSlots; i++){ if (frame.isOperandRef(i)){ ElementInfo ei = ti.getElementInfo(frame.peek(FeatureExprFactory.True(), i).getValue()); if (ei != null){ if (ei.getObjectAttr(type) != null){ return true; } } } } return false; } // we get this from the caller because this works both for pre- and post-exec // notifications, whereas retrieval from the callee frame of course only works // post-exec Object[] getArgsFromCaller (ThreadInfo ti, StackFrame frame, MethodInfo callee){ int n = callee.getNumberOfArguments(); Object[] args = new Object[n]; byte[] at = callee.getArgumentTypes(); for (int i=n-1, off=0; i>=0; i--) { switch (at[i]) { case Types.T_ARRAY: //case Types.T_OBJECT: case Types.T_REFERENCE: int ref = frame.peek(FeatureExprFactory.True(), off).getValue(); if (ref != MJIEnv.NULL) { args[i] = ti.getElementInfo(ref); } else { args[i] = null; } off++; break; case Types.T_LONG: args[i] = frame.peekLong(FeatureExprFactory.True(), off).getValue(); off+=2; break; case Types.T_DOUBLE: args[i] = Double.valueOf(Types.longToDouble(frame.peekLong(FeatureExprFactory.True(), off).getValue())); off+=2; break; case Types.T_BOOLEAN: args[i] = Boolean.valueOf(frame.peek(FeatureExprFactory.True(), off).getValue().intValue() != 0); off++; break; case Types.T_BYTE: args[i] = Byte.valueOf((byte)frame.peek(FeatureExprFactory.True(), off).getValue().intValue()); off++; break; case Types.T_CHAR: args[i] = Character.valueOf((char)frame.peek(FeatureExprFactory.True(), off).getValue().intValue()); off++; break; case Types.T_SHORT: args[i] = Short.valueOf((short)frame.peek(FeatureExprFactory.True(), off).getValue().intValue()); off++; break; case Types.T_INT: args[i] = frame.peek(FeatureExprFactory.True(), off).getValue(); off++; break; case Types.T_FLOAT: args[i] = Float.valueOf(Types.intToFloat(frame.peek(FeatureExprFactory.True(), off).getValue().intValue())); off++; break; default: // error, unknown argument type } } return args; } public int getArgSize () { if (argSize < 0) { argSize = Types.getArgumentsSize(signature) + 1; // 'this' } return argSize; } public int getReturnType() { return Types.getReturnBuiltinType(signature); } public boolean isReferenceReturnType() { int r = Types.getReturnBuiltinType(signature); return ((r == Types.T_REFERENCE) || (r == Types.T_ARRAY)); } public String getReturnTypeName() { return Types.getReturnTypeName(signature); } public Object getFieldOrArgumentValue (String id, ThreadInfo ti){ Object v = null; if ((v = getArgumentValue(id,ti)) == null){ v = getFieldValue(id, ti); } return v; } public abstract Object getFieldValue (String id, ThreadInfo ti); /** * <2do> - this relies on same order of arguments and LocalVariableTable entries, which * seems to hold for javac, but is not required by the VM spec, which only * says that arguments are stored in consecutive slots starting at 0 */ public Object getArgumentValue (String id, ThreadInfo ti){ MethodInfo mi = getInvokedMethod(); LocalVarInfo localVars[] = mi.getLocalVars(); Object[] args = getArgumentValues(ti); if (localVars != null){ int j = mi.isStatic() ? 0 : 1; for (int i=0; i<args.length; i++, j++){ Object a = args[i]; if (id.equals(localVars[j].getName())){ return a; } } } return null; } protected boolean checkSyncCG (ElementInfo ei, ThreadInfo ti){ if (!ti.isFirstStepInsn()) { if (ei.getLockingThread() != ti) { // maybe its a recursive lock VM vm = ti.getVM(); if (ei.canLock(ti)) { // we can lock the object, check if we need a CG if (ei.isShared()) { ChoiceGenerator<?> cg = vm.getSchedulerFactory().createSyncMethodEnterCG(ei, ti); if (cg != null) { if (vm.setNextChoiceGenerator(cg)) { ei = ei.getModifiableInstance(); ei.registerLockContender(ti); // Record that this thread would lock the object upon next execution return true; } } } } else { // already locked by another thread, we have to block and therefore need a CG ei = ei.getModifiableInstance(); // the top half already did set the object shared ei.block(ti); // do this before we obtain the CG so that this thread is not in its choice set ChoiceGenerator<?> cg = vm.getSchedulerFactory().createSyncMethodEnterCG(ei, ti); vm.setMandatoryNextChoiceGenerator(cg, "blocking sync without CG"); return true; } } } return false; } public void accept(InstructionVisitor insVisitor) { insVisitor.visit(this); } @Override public Instruction typeSafeClone(MethodInfo mi) { InvokeInstruction clone = null; try { clone = (InvokeInstruction) super.clone(); // reset the method that this insn belongs to clone.mi = mi; clone.invokedMethod = null; } catch (CloneNotSupportedException e) { e.printStackTrace(); } return clone; } }