// // Copyright (C) 2010 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.vm; import java.io.PrintWriter; import java.io.StringWriter; import java.util.function.Function; import de.fosd.typechef.featureexpr.FeatureExpr; import gov.nasa.jpf.JPFException; import gov.nasa.jpf.jvm.bytecode.NATIVERETURN; import gov.nasa.jpf.util.HashData; import gov.nasa.jpf.util.Misc; /** * 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 (FeatureExpr ctx, NativeMethodInfo mi){ super( ctx, mi, 0, 0); } public void setArgs (Object[] args){ this.args = args; } 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(FeatureExpr ctx) { 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; } public void markThreadRoots (final Heap heap, final 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 pc.map(new Function<Instruction, Object>() { @Override public Object apply(Instruction pc) { if (pc instanceof NATIVERETURN){ if (ret != null && ret instanceof Integer && mi.isReferenceReturnType()) { int ref = ((Integer) ret).intValue(); heap.markThreadRoot(ref, tid); } } return null; } }); } 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); } } 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; } 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, FeatureExpr ctx){ 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"); } 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"); } } public int getReferenceResult(FeatureExpr ctx){ 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"); } } 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"); } } public Object getResultAttr(){ return retAttr; } public Object getLongResultAttr(){ return retAttr; } }