view src/main/gov/nasa/jpf/jvm/bytecode/JVMInvokeInstruction.java @ 27:8aded593a50f

fixed the missing class init status update for native clinits. Since we moved that to the clinit RETURN in r24, it has to be duplicated in NATIVERETURN for native clinits. Factoring out to DIRECTCALLRETURN is not an option since there is no good way to determine if the direct call included a clinit (we would have to look at the code, which is suboptimal performance- and change resilience-wise). Thanks to Franz Weitl for the report
author Peter Mehlitz <pcmehlitz@gmail.com>
date Mon, 11 May 2015 12:17:18 -0700
parents 61d41facf527
children
line wrap: on
line source

/*
 * Copyright (C) 2014, United States Government, as represented by the
 * Administrator of the National Aeronautics and Space Administration.
 * All rights reserved.
 *
 * The Java Pathfinder core (jpf-core) platform is licensed under the
 * Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 *        http://www.apache.org/licenses/LICENSE-2.0. 
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 */
package gov.nasa.jpf.jvm.bytecode;

import gov.nasa.jpf.vm.ClassInfo;
import gov.nasa.jpf.vm.Instruction;
import gov.nasa.jpf.vm.LocalVarInfo;
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.bytecode.InvokeInstruction;


/**
 * abstraction for all invoke instructions
 */
public abstract class JVMInvokeInstruction extends InvokeInstruction implements JVMInstruction {
  /* Those are all 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 JVMInvokeInstruction (String clsName, String methodName, String signature){
    this.cname = Types.getClassNameFromTypeName(clsName);
    this.signature = signature;
    this.mname = MethodInfo.getUniqueName(methodName, signature);
  }

  protected JVMInvokeInstruction () {}

  @Override
  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
   */
  @Override
  public String getInvokedMethodClassName() {
    return cname;
  }

  @Override
  public String getInvokedMethodSignature() {
    return signature;
  }

  @Override
  public String getInvokedMethodName () {
    return mname;
  }

  public abstract MethodInfo getInvokedMethod (ThreadInfo ti);

  @Override
  public MethodInfo getInvokedMethod () {
    if (invokedMethod == null){
      invokedMethod = getInvokedMethod(ThreadInfo.getCurrentThread());
    }

    return invokedMethod;
  }

  @Override
  public boolean isCompleted(ThreadInfo ti) {
    Instruction nextPc = ti.getNextPC();

    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 (ThreadInfo ti, MethodInfo callee){
    ClassInfo ciCaller = callee.getClassInfo();
    StackFrame frame = ciCaller.createStackFrame( 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 JVMInvokeInstruction
   * 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(ti);
    StackFrame frame = getCallerFrame(ti, callee);
    
    assert frame != null : "can't find caller stackframe for: " + this;
    return frame.getCallArguments(ti);
  }

  public Object[] getArgumentAttrs (ThreadInfo ti) {
    MethodInfo callee = getInvokedMethod(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(ti);
    StackFrame frame = getCallerFrame(ti, callee);

    assert frame != null : "no 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 hasArgumentObjectAttr (ThreadInfo ti, Class<?> type){
    MethodInfo callee = getInvokedMethod(ti);
    StackFrame frame = getCallerFrame(ti, callee);

    assert frame != null : "no caller stackframe for: " + this;
    return frame.hasArgumentObjectAttr(ti,callee,type);
  }

  /**
   * this is slot size, i.e. includes 'this' for InstanceInvocations 
   */
  abstract public int getArgSize();
  
  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;
  }
    
  @Override
  public void accept(JVMInstructionVisitor insVisitor) {
	  insVisitor.visit(this);
  }

  @Override
  public Instruction typeSafeClone(MethodInfo mi) {
    JVMInvokeInstruction clone = null;

    try {
      clone = (JVMInvokeInstruction) super.clone();

      // reset the method that this insn belongs to
      clone.mi = mi;

      clone.invokedMethod = null;
    } catch (CloneNotSupportedException e) {
      e.printStackTrace();
    }

    return clone;
  }
}