//
// 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 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.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.StaticElementInfo;
import gov.nasa.jpf.vm.ThreadInfo;
import gov.nasa.jpf.vm.Types;
/**
* Invoke a class (static) method
* ..., [arg1, [arg2 ...]] => ...
*/
public class INVOKESTATIC extends InvokeInstruction {
ClassInfo ci;
protected INVOKESTATIC (String clsDescriptor, String methodName, String signature){
super(clsDescriptor, methodName, signature);
}
protected ClassInfo getClassInfo () {
if (ci == null) {
ci = ClassLoaderInfo.getCurrentResolvedClassInfo(cname);
}
return ci;
}
public int getByteCode () {
return 0xB8;
}
public StaticElementInfo getStaticElementInfo (){
return getClassInfo().getStaticElementInfo();
}
public int getClassObjectRef(){
return getClassInfo().getStaticElementInfo().getClassObjectRef();
}
public Conditional<Instruction> execute (FeatureExpr ctx, ThreadInfo ti) {
MethodInfo callee;
try {
callee = getInvokedMethod(ctx, ti);
} catch (LoadOnJPFRequired lre) {
return ti.getPC();
}
if (callee == null) {
return new One<>(ti.createAndThrowException(ctx, "java.lang.NoSuchMethodException", cname + '.' + mname));
}
// this can be actually different than (can be a base)
ClassInfo ciCallee = callee.getClassInfo();
if ( ciCallee.pushRequiredClinits(ctx, ti)) {
// do class initialization before continuing
// note - this returns the next insn in the topmost clinit that just got pushed
return ti.getPC();
}
if (callee.isSynchronized()) {
ElementInfo ei = ciCallee.getClassObject();
ei = ei.getInstanceWithUpdatedSharedness(ti);
if (checkSyncCG(ei, ti)){
return new One<Instruction>(this);
}
}
// set ctx for native method calls
// boolean splitRef = false;
// if (callee.isMJI()) {
// IStackHandler stack = ti.getTopFrame().stack;
// if (stack.getStackWidth() > 1) {
// boolean split = false;
// for (int i = 0; i < callee.getNumberOfArguments(); i++) {
// if (stack.peek(ctx, i) instanceof IChoice) {
// split = true;
// splitRef = true;
// break;
// }
// }
//
// if (split) {
// Map<Stack, FeatureExpr> stacks = stack.getStack().simplify(ctx).toMap();
// for (FeatureExpr c : stacks.values()) {
// ctx = ctx.and(c);
// break;
// }
// }
// }
// }
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<Instruction>(this)).simplify(); // we can't just return the first callee insn
// if a listener throws an exception
}
public MethodInfo getInvokedMethod(){
if (invokedMethod != null){
return invokedMethod;
} else {
// Hmm, this would be pre-exec, but if the current thread is not the one executing the insn
// this might result in false sharedness of the class object
return getInvokedMethod( FeatureExprFactory.True(), ThreadInfo.getCurrentThread());
}
}
public MethodInfo getInvokedMethod (FeatureExpr ctx, ThreadInfo ti){
if (invokedMethod == null) {
ClassInfo clsInfo = getClassInfo();
if (clsInfo != null){
MethodInfo callee = clsInfo.getMethod(mname, true);
if (callee == null) {
throw new RuntimeException("Method " + mname + " in class " + clsInfo + " not found");
}
ClassInfo ciCallee = callee.getClassInfo(); // might be a superclass of ci, i.e. not what is referenced in the insn
if (!ciCallee.isRegistered()){
// if it wasn't registered yet, classLoaded listeners didn't have a chance yet to modify it..
ciCallee.registerClass(ctx, ti);
// .. and might replace/remove MethodInfos
callee = clsInfo.getMethod(mname, true);
}
invokedMethod = callee;
}
}
return invokedMethod;
}
// can be different thatn the ci - method can be in a superclass
public ClassInfo getInvokedClassInfo(){
return getInvokedMethod().getClassInfo();
}
public String getInvokedClassName(){
return getInvokedClassInfo().getName();
}
public int getArgSize () {
if (argSize < 0) {
argSize = Types.getArgumentsSize(signature);
}
return argSize;
}
public String toString() {
// methodInfo not set outside real call context (requires target object)
return super.toString() + " " + cname + '.' + mname;
}
public Object getFieldValue (String id, ThreadInfo ti) {
return getClassInfo().getStaticFieldValueObject(id);
}
public void accept(InstructionVisitor insVisitor) {
insVisitor.visit(this);
}
@Override
public Instruction typeSafeClone(MethodInfo mi) {
INVOKESTATIC clone = null;
try {
clone = (INVOKESTATIC) super.clone();
// reset the method that this insn belongs to
clone.mi = mi;
clone.invokedMethod = null;
clone.lastObj = Integer.MIN_VALUE;
clone.ci = null;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}