view src/main/gov/nasa/jpf/vm/NativeStackFrame.java @ 17:e15b03204dc7

added a @NoJPFExecution annotation, which sets a NoJPFExec system attr on marked methods during class load time, which gets checked by ThreadInfo.enter(). Useful to flag methods which have to be intercepted/cut off when executing classes under JPF that can also be used outside. Especially useful to avoid the recursive JPF problem that can be caused by tests (which mix classpath and native_classpath). This currently throws a JPFException, but we could also turn this into a AssertionError in the SUT so that we get the SUT stack trace
author Peter Mehlitz <pcmehlitz@gmail.com>
date Mon, 23 Mar 2015 12:54:20 -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.vm;

import gov.nasa.jpf.JPFException;
import gov.nasa.jpf.jvm.bytecode.NATIVERETURN;
import gov.nasa.jpf.util.HashData;
import gov.nasa.jpf.util.Misc;

import java.io.PrintWriter;
import java.io.StringWriter;

/**
 * a stack frame for MJI methods
 * 
 * This is a special Stackframe to execute NativeMethodInfos, which are just a wrapper around Java reflection
 * calls. As required by the Java reflection API, they can store argument and return values as object references
 *
 * NOTE: operands and locals can be, but are not automatically used during
 * native method execution.
 */
public abstract class NativeStackFrame extends StackFrame {


  // we don't use the operand stack or locals for arguments and return value
  // because (1) they don't have the right representation (host VM),
  // (2) for performance reasons (no array alloc), and (3) because there is no
  // choice point once we enter a native method, so there is no need to do
  // copy-on-write on the ThreadInfo callstack. Native method execution is
  // atomic (leave alone roundtrips of course)

  // return value registers
  protected Object ret;
  protected Object retAttr;

  // our argument registers
  protected Object[] args;

  public NativeStackFrame (NativeMethodInfo mi){
    super( mi, 0, 0);
  }
  
  public void setArgs (Object[] args){
    this.args = args; 
  }

  @Override
  public StackFrame clone () {
    NativeStackFrame sf = (NativeStackFrame) super.clone();

    if (args != null) {
      sf.args = args.clone();
    }

    return sf;
  }

  @Override
  public boolean isNative() {
    return true;
  }

  @Override
  public boolean isSynthetic() {
    return true;
  }

  @Override
  public boolean modifiesState() {
    // native stackframes don't do anything with their operands or locals per se
    // they are executed atomically, so there is no need to ever restore them
    return false;
  }

  @Override
  public boolean hasAnyRef() {
    return false;
  }

  public void setReturnAttr (Object a){
    retAttr = a;
  }

  public void setReturnValue(Object r){
    ret = r;
  }

  public void clearReturnValue() {
    ret = null;
    retAttr = null;
  }

  public Object getReturnValue() {
    return ret;
  }

  public Object getReturnAttr() {
    return retAttr;
  }

  public Object[] getArguments() {
    return args;
  }

  @Override
  public void markThreadRoots (Heap heap, int tid) {
    // what if some listener creates a CG post-EXECUTENATIVE or pre-NATIVERETURN?
    // and the native method returned an object?
    // on the other hand, we have to make sure we don't mark a return value from
    // a previous transition

    if (pc instanceof NATIVERETURN){
      if (ret != null && ret instanceof Integer && mi.isReferenceReturnType()) {
        int ref = ((Integer) ret).intValue();
        heap.markThreadRoot(ref, tid);
      }
    }
  }

  @Override
  protected void hash (HashData hd) {
    super.hash(hd);

    if (ret != null){
      hd.add(ret);
    }
    if (retAttr != null){
      hd.add(retAttr);
    }

    for (Object a : args){
      hd.add(a);
    }
  }

  @Override
  public boolean equals (Object object) {
    if (object == null || !(object instanceof NativeStackFrame)){
      return false;
    }

    if (!super.equals(object)){
      return false;
    }

    NativeStackFrame o = (NativeStackFrame)object;

    if (ret != o.ret){
      return false;
    }
    if (retAttr != o.retAttr){
      return false;
    }

    if (args.length != o.args.length){
      return false;
    }

    if (!Misc.compare(args.length, args, o.args)){
      return false;
    }

    return true;
  }

  @Override
  public String toString () {
    StringWriter sw = new StringWriter(128);
    PrintWriter pw = new PrintWriter(sw);

    pw.print("NativeStackFrame@");
    pw.print(Integer.toHexString(objectHashCode()));
    pw.print("{ret=");
    pw.print(ret);
    if (retAttr != null){
      pw.print('(');
      pw.print(retAttr);
      pw.print(')');
    }
    pw.print(',');
    printContentsOn(pw);
    pw.print('}');

    return sw.toString();
  }
  
  //--- NativeStackFrames aren't called directly and have special return value processing (in NATIVERETURN.execute())
  @Override
  public void setArgumentLocal (int idx, int value, Object attr){
    throw new JPFException("NativeStackFrames don't support setting argument locals");
  }
  @Override
  public void setLongArgumentLocal (int idx, long value, Object attr){
    throw new JPFException("NativeStackFrames don't support setting argument locals");    
  }
  @Override
  public void setReferenceArgumentLocal (int idx, int ref, Object attr){
    throw new JPFException("NativeStackFrames don't support setting argument locals");
  }
  
  //--- exception refs
  @Override
  public void setExceptionReference (int exRef){
    throw new JPFException("NativeStackFrames don't support exception handlers");    
  }

  @Override
  public int getExceptionReference (){
    throw new JPFException("NativeStackFrames don't support exception handlers");    
  }

  @Override
  public void setExceptionReferenceAttribute (Object attr){
    throw new JPFException("NativeStackFrames don't support exception handlers");    
  }
  
  @Override
  public Object getExceptionReferenceAttribute (){
    throw new JPFException("NativeStackFrames don't support exception handlers");    
  }
  
  @Override
  public int getResult(){
    Object r = ret;
    
    if (r instanceof Number){
      if (r instanceof Double){
        throw new JPFException("result " + ret + " can't be converted into int");    
      } else if (r instanceof Float){
        return Float.floatToIntBits((Float)r);
      } else {
        return ((Number)r).intValue();
      }
    } else if (r instanceof Boolean){
      return (r == Boolean.TRUE) ? 1 : 0;
    } else {
      throw new JPFException("result " + ret + " can't be converted into raw int value");
    }
  }
  
  @Override
  public int getReferenceResult(){
    if (ret instanceof Integer){
      return (Integer)ret; // MJI requires references to be returned as 'int'
    } else {
      throw new JPFException("result " + ret + " can't be converted into JPF refrence value");      
    }
  }
  
  @Override
  public long getLongResult(){
    Object r = ret;
    if (r instanceof Long){
      return (Long)r;
    } else if (r instanceof Double){
      return Double.doubleToLongBits((Double)r);
    } else {
      throw new JPFException("result " + ret + " can't be converted into raw long value");      
    }
  }
  
  @Override
  public Object getResultAttr(){
    return retAttr;
  }
  @Override
  public Object getLongResultAttr(){
    return retAttr;    
  }
}