Mercurial > hg > Members > kono > jpf-core
view src/main/gov/nasa/jpf/vm/StackFrame.java @ 0:61d41facf527
initial v8 import (history reset)
author | Peter Mehlitz <Peter.C.Mehlitz@nasa.gov> |
---|---|
date | Fri, 23 Jan 2015 10:14:01 -0800 |
parents | |
children | 3517702bd768 |
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.util.BitSetN; import gov.nasa.jpf.util.BitSet1024; import gov.nasa.jpf.util.BitSet256; import gov.nasa.jpf.util.BitSet64; import gov.nasa.jpf.util.FixedBitSet; import gov.nasa.jpf.util.HashData; import gov.nasa.jpf.util.Misc; import gov.nasa.jpf.util.OATHash; import gov.nasa.jpf.util.ObjectList; import gov.nasa.jpf.util.PrintUtils; import gov.nasa.jpf.vm.bytecode.InvokeInstruction; import java.io.PrintStream; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Iterator; /** * Describes callerSlots stack frame. * * Java methods always have bounded local and operand stack sizes, computed * at compile time, stored in the classfile, and checked at runtime by the * bytecode verifier. Consequently, we combine locals and operands in one * data structure with the following layout * * slot[0] : 'this' * .. .. local vars * slot[stackBase-1] : last local var * slot[stackBase] : first operand slot * .. ^ * .. | operand stack range * .. v * slot[top] : highest used operand slot * */ public abstract class StackFrame implements Cloneable { /** * this StackFrame is not allowed to be modified anymore because it has been state stored. * Set during state storage and checked upon each modification, causing exceptions on attempts * to modify callerSlots frozen instance. The flag is reset in clones */ public static final int ATTR_IS_FROZEN = 0x100; static final int ATTR_IS_REFLECTION = 0x1000; /** * the previous StackFrame (usually the caller, null if first). To be set when * the frame is pushed on the ThreadInfo callstack */ protected StackFrame prev; /** * state management related attributes similar to ElementInfo. The lower 16 bits * are stored/restored, the upper 16 bits are for transient use */ protected int attributes; protected int top; // top index of the operand stack (NOT size) // this points to the last pushed value protected int thisRef = MJIEnv.NULL; // slots[0] can change, but we have to keep 'this' protected int stackBase; // index where the operand stack begins protected int[] slots; // the combined local and operand slots protected FixedBitSet isRef; // which slots contain references protected Object frameAttr; // optional user attrs for the whole frame /* * This array can be used to store attributes (e.g. variable names) for * operands. We don't do anything with this except of preserving it (across * dups etc.), so it's pretty much up to the VM listeners/peers what's stored * * NOTE: attribute values are not restored upon backtracking per default, but * attribute references are. If you need restoration of values, use copy-on-write * in your clients * * these are set on demand */ protected Object[] attrs = null; // the combined user-defined callerSlots (set on demand) protected Instruction pc; // the next insn to execute (program counter) protected MethodInfo mi; // which method is executed in this frame static final int[] EMPTY_ARRAY = new int[0]; static final FixedBitSet EMPTY_BITSET = new BitSet64(); protected StackFrame (MethodInfo callee, int nLocals, int nOperands){ mi = callee; pc = mi.getInstruction(0); stackBase = nLocals; top = nLocals-1; int nSlots = nLocals + nOperands; if (nSlots > 0){ slots = new int[nLocals + nOperands]; isRef = createReferenceMap(slots.length); } else { // NativeStackFrames don't use locals or operands, but we // don't want to add tests to all our methods slots = EMPTY_ARRAY; isRef = EMPTY_BITSET; } } public StackFrame (MethodInfo callee){ this( callee, callee.getMaxLocals(), callee.getMaxStack()); } /** * Creates an empty stack frame. Used by clone. */ protected StackFrame () { } /** * creates callerSlots dummy Stackframe for testing of operand/local operations * NOTE - TESTING ONLY! this does not have a MethodInfo */ protected StackFrame (int nLocals, int nOperands){ stackBase = nLocals; slots = new int[nLocals + nOperands]; isRef = createReferenceMap(slots.length); top = nLocals-1; // index, not size! } /** * re-execute method from the beginning - use with care */ public void reset() { pc = mi.getInstruction(0); } protected FixedBitSet createReferenceMap (int nSlots){ if (nSlots <= 64){ return new BitSet64(); } else if (nSlots <= 256){ return new BitSet256(); } else if (nSlots <= 1024) { return new BitSet1024(); } else { return new BitSetN(nSlots); } } public boolean isNative() { return false; } public StackFrame getCallerFrame (){ MethodInfo callee = mi; for (StackFrame frame = getPrevious(); frame != null; frame = frame.getPrevious()){ Instruction insn = frame.getPC(); if (insn instanceof InvokeInstruction){ InvokeInstruction call = (InvokeInstruction)insn; if (call.getInvokedMethod() == callee){ return frame; } } } return null; } /** * return the object reference for an instance method to be called (we are still in the * caller's frame). This only makes sense after all params have been pushed, before the * INVOKEx insn is executed */ public int getCalleeThis (MethodInfo mi) { return getCalleeThis(mi.getArgumentsSize()); } /** * return reference of called object in the context of the caller * (i.e. we are in the caller frame) */ public int getCalleeThis (int size) { // top is the topmost index int i = size-1; if (top < i) { return MJIEnv.NULL; } return slots[top-i]; } public StackFrame getPrevious() { return prev; } /** * to be set (by ThreadInfo) when the frame is pushed. Can also be used * for non-local gotos, but be warned - that's tricky */ public void setPrevious (StackFrame frame){ prev = frame; } public Object getLocalOrFieldValue (String id) { // try locals first LocalVarInfo lv = mi.getLocalVar(id, pc.getPosition()); if (lv != null){ return getLocalValueObject(lv); } // then fields return getFieldValue(id); } public Object getLocalValueObject (LocalVarInfo lv) { if (lv != null) { // might not have been compiled with debug info String sig = lv.getSignature(); int slotIdx = lv.getSlotIndex(); int v = slots[slotIdx]; switch (sig.charAt(0)) { case 'Z': return Boolean.valueOf(v != 0); case 'B': return new Byte((byte) v); case 'C': return new Character((char) v); case 'S': return new Short((short) v); case 'I': return new Integer(v); case 'J': return new Long(Types.intsToLong(slots[slotIdx + 1], v)); // Java is big endian, Types expects low,high case 'F': return new Float(Float.intBitsToFloat(v)); case 'D': return new Double(Double.longBitsToDouble(Types.intsToLong(slots[slotIdx + 1], v))); default: // reference if (v >= 0) { return VM.getVM().getHeap().get(v); } } } return null; } public Object getFieldValue (String id) { // try instance fields first if (thisRef != MJIEnv.NULL) { // it's an instance method ElementInfo ei = VM.getVM().getHeap().get(thisRef); Object v = ei.getFieldValueObject(id); if (v != null) { return v; } } // check static fields (in method class and its superclasses) return mi.getClassInfo().getStaticFieldValueObject(id); } public ClassInfo getClassInfo () { return mi.getClassInfo(); } public String getClassName () { return mi.getClassInfo().getName(); } public String getSourceFile () { return mi.getClassInfo().getSourceFileName(); } /** * does any of the 'nTopSlots' hold callerSlots reference value of 'objRef' * 'nTopSlots' is usually obtained from MethodInfo.getNumberOfCallerStackSlots() */ public boolean includesReferenceOperand (int nTopSlots, int objRef){ for (int i=0, j=top-nTopSlots+1; i<nTopSlots && j>=0; i++, j++) { if (isRef.get(j) && (slots[j] == objRef)){ return true; } } return false; } /** * does any of the operand slots hold callerSlots reference value of 'objRef' */ public boolean includesReferenceOperand (int objRef){ for (int i=stackBase; i<=top; i++) { if (isRef.get(i) && (slots[i] == objRef)){ return true; } } return false; } /** * is this StackFrame modifying the KernelState * this is true unless this is callerSlots NativeStackFrame */ public boolean modifiesState() { return true; } public boolean isDirectCallFrame () { return false; } public boolean isSynthetic() { return false; } // gets and sets some derived information public int getLine () { return mi.getLineNumber(pc); } /** * generic visitor for reference arguments */ public void processRefArguments (MethodInfo miCallee, ReferenceProcessor visitor){ int nArgSlots = miCallee.getArgumentsSize(); for (int i=top-1; i>=top-nArgSlots; i--){ if (isRef.get(i)){ visitor.processReference(slots[i]); } } } public int getSlot(int idx){ return slots[idx]; } public boolean isReferenceSlot(int idx){ return isRef.get(idx); } public void setOperand (int offset, int v, boolean isRefValue){ int i = top-offset; slots[i] = v; isRef.set(i, isRefValue); } //----------------------------- various attribute accessors public boolean hasAttrs () { return attrs != null; } public boolean hasFrameAttr(){ return frameAttr != null; } public boolean hasFrameAttr (Class<?> attrType){ return ObjectList.containsType(frameAttr, attrType); } public boolean hasFrameAttrValue (Object a){ return ObjectList.contains(frameAttr, a); } //--- the frame attr accessors /** * this returns all of them - use either if you know there will be only * one attribute at callerSlots time, or check/process result with ObjectList */ public Object getFrameAttr(){ return frameAttr; } /** * this replaces all of them - use only if you know there are no * SystemAttributes in the list (which would cause an exception) */ public void setFrameAttr (Object attr){ frameAttr = ObjectList.set(frameAttr, attr); } public void addFrameAttr (Object attr){ frameAttr = ObjectList.add(frameAttr, attr); } public void removeFrameAttr (Object attr){ frameAttr = ObjectList.remove(frameAttr, attr); } public void replaceFrameAttr (Object oldAttr, Object newAttr){ frameAttr = ObjectList.replace(frameAttr, oldAttr, newAttr); } /** * this only returns the first attr of this type, there can be more * if you don't use client private types or the provided type is too general */ public <T> T getFrameAttr (Class<T> attrType) { return ObjectList.getFirst(frameAttr, attrType); } public <T> T getAndResetFrameAttr (Class<T> attrType) { T attr = ObjectList.getFirst(frameAttr, attrType); if (attr != null){ frameAttr = ObjectList.remove(frameAttr, attr); } return attr; } public <T> T getNextFrameAttr (Class<T> attrType, Object prev) { return ObjectList.getNext(frameAttr, attrType, prev); } public ObjectList.Iterator frameAttrIterator(){ return ObjectList.iterator(frameAttr); } public <T> ObjectList.TypedIterator<T> frameAttrIterator(Class<T> attrType){ return ObjectList.typedIterator(frameAttr, attrType); } //--- the top single-slot operand attrs public boolean hasOperandAttr(){ if ((top >= stackBase) && (attrs != null)){ return (attrs[top] != null); } return false; } public boolean hasOperandAttr(Class<?> type){ if ((top >= stackBase) && (attrs != null)){ return ObjectList.containsType(attrs[top], type); } return false; } /** * this returns all of them - use either if you know there will be only * one attribute at callerSlots time, or check/process result with ObjectList */ public Object getOperandAttr () { if ((top >= stackBase) && (attrs != null)){ return attrs[top]; } return null; } /** * this replaces all of them - use only if you know * - there will be only one attribute at callerSlots time * - you obtained the value you set by callerSlots previous getXAttr() * - you constructed callerSlots multi value list with ObjectList.createList() */ public void setOperandAttr (Object a){ assert (top >= stackBase); if (attrs == null) { if (a == null) return; attrs = new Object[slots.length]; } attrs[top] = a; } /** * this only returns the first attr of this type, there can be more * if you don't use client private types or the provided type is too general */ public <T> T getOperandAttr (Class<T> attrType){ assert (top >= stackBase); if ((attrs != null)){ return ObjectList.getFirst( attrs[top], attrType); } return null; } public <T> T getNextOperandAttr (Class<T> attrType, Object prev){ assert (top >= stackBase); if (attrs != null){ return ObjectList.getNext( attrs[top], attrType, prev); } return null; } public Iterator operandAttrIterator(){ assert (top >= stackBase); Object a = (attrs != null) ? attrs[top] : null; return ObjectList.iterator(a); } public <T> Iterator<T> operandAttrIterator(Class<T> attrType){ assert (top >= stackBase); Object a = (attrs != null) ? attrs[top] : null; return ObjectList.typedIterator(a, attrType); } public void addOperandAttr (Object a){ assert (top >= stackBase); if (a != null){ if (attrs == null) { attrs = new Object[slots.length]; } attrs[top] = ObjectList.add(attrs[top], a); } } public void removeOperandAttr (Object a){ assert (top >= stackBase) && (a != null); if (attrs != null){ attrs[top] = ObjectList.remove(attrs[top], a); } } public void replaceOperandAttr (Object oldAttr, Object newAttr){ assert (top >= stackBase) && (oldAttr != null) && (newAttr != null); if (attrs != null){ attrs[top] = ObjectList.replace(attrs[top], oldAttr, newAttr); } } //--- offset operand attrs public boolean hasOperandAttr(int offset){ int i = top-offset; assert (i >= stackBase); if (attrs != null){ return (attrs[i] != null); } return false; } public boolean hasOperandAttr(int offset, Class<?> type){ int i = top-offset; assert (i >= stackBase); if (attrs != null){ return ObjectList.containsType(attrs[i], type); } return false; } /** * this returns all of them - use either if you know there will be only * one attribute at callerSlots time, or check/process result with ObjectList */ public Object getOperandAttr (int offset) { int i = top-offset; assert (i >= stackBase); if (attrs != null) { return attrs[i]; } return null; } /** * this replaces all of them - use only if you know * - there will be only one attribute at callerSlots time * - you obtained the value you set by callerSlots previous getXAttr() * - you constructed callerSlots multi value list with ObjectList.createList() */ public void setOperandAttr (int offset, Object a){ int i = top-offset; assert (i >= stackBase); if (attrs == null) { if (a == null) return; attrs = new Object[slots.length]; } attrs[i] = a; } /** * this only returns the first attr of this type, there can be more * if you don't use client private types or the provided type is too general */ public <T> T getOperandAttr (int offset, Class<T> attrType){ int i = top-offset; assert (i >= stackBase); if (attrs != null){ return ObjectList.getFirst( attrs[i], attrType); } return null; } public <T> T getNextOperandAttr (int offset, Class<T> attrType, Object prev){ int i = top-offset; assert (i >= stackBase); if (attrs != null){ return ObjectList.getNext( attrs[i], attrType, prev); } return null; } public ObjectList.Iterator operandAttrIterator(int offset){ int i = top-offset; assert (i >= stackBase); Object a = (attrs != null) ? attrs[i] : null; return ObjectList.iterator(a); } public <T> ObjectList.TypedIterator<T> operandAttrIterator(int offset, Class<T> attrType){ int i = top-offset; assert (i >= stackBase); Object a = (attrs != null) ? attrs[i] : null; return ObjectList.typedIterator(a, attrType); } public void addOperandAttr (int offset, Object a){ int i = top-offset; assert (i >= stackBase); if (a != null){ if (attrs == null) { attrs = new Object[slots.length]; } attrs[i] = ObjectList.add(attrs[i],a); } } public void removeOperandAttr (int offset, Object a){ int i = top-offset; assert (i >= stackBase) && (a != null); if (attrs != null){ attrs[i] = ObjectList.remove(attrs[i], a); } } public void replaceOperandAttr (int offset, Object oldAttr, Object newAttr){ int i = top-offset; assert (i >= stackBase) && (oldAttr != null) && (newAttr != null); if (attrs != null){ attrs[i] = ObjectList.replace(attrs[i], oldAttr, newAttr); } } //--- top double-slot operand attrs // we store attributes for double slot values at the local var index, // which is the lower one. The ..LongOperand.. APIs are handling this offset public boolean hasLongOperandAttr(){ return hasOperandAttr(1); } public boolean hasLongOperandAttr(Class<?> type){ return hasOperandAttr(1, type); } /** * this returns all of them - use either if you know there will be only * one attribute at callerSlots time, or check/process result with ObjectList */ public Object getLongOperandAttr () { return getOperandAttr(1); } /** * this replaces all of them - use only if you know * - there will be only one attribute at callerSlots time * - you obtained the value you set by callerSlots previous getXAttr() * - you constructed callerSlots multi value list with ObjectList.createList() */ public void setLongOperandAttr (Object a){ setOperandAttr(1, a); } /** * this only returns the first attr of this type, there can be more * if you don't use client private types or the provided type is too general */ public <T> T getLongOperandAttr (Class<T> attrType) { return getOperandAttr(1, attrType); } public <T> T getNextLongOperandAttr (Class<T> attrType, Object prev) { return getNextOperandAttr(1, attrType, prev); } public ObjectList.Iterator longOperandAttrIterator(){ return operandAttrIterator(1); } public <T> ObjectList.TypedIterator<T> longOperandAttrIterator(Class<T> attrType){ return operandAttrIterator(1, attrType); } public void addLongOperandAttr (Object a){ addOperandAttr(1, a); } public void removeLongOperandAttr (Object a){ removeOperandAttr(1, a); } public void replaceLongOperandAttr (Object oldAttr, Object newAttr){ replaceOperandAttr(1, oldAttr, newAttr); } //--- local attrs // single- or double-slot - you have to provide the var index anyways) public boolean hasLocalAttr(int index){ assert index < stackBase; if (attrs != null){ return (attrs[index] != null); } return false; } public boolean hasLocalAttr(int index, Class<?> type){ assert index < stackBase; if (attrs != null){ return ObjectList.containsType(attrs[index], type); } return false; } /** * this returns all of them - use either if you know there will be only * one attribute at callerSlots time, or check/process result with ObjectList */ public Object getLocalAttr (int index){ assert index < stackBase; if (attrs != null){ return attrs[index]; } return null; } public Object getLongLocalAttr (int index){ return getLocalAttr( index); } /** * this replaces all of them - use only if you know * - there will be only one attribute at callerSlots time * - you obtained the value you set by callerSlots previous getXAttr() * - you constructed callerSlots multi value list with ObjectList.createList() */ public void setLocalAttr (int index, Object a) { assert index < stackBase; if (attrs == null){ if (a == null) return; attrs = new Object[slots.length]; } attrs[index] = a; } public void setLongLocalAttr (int index, Object a){ setLocalAttr( index, a); } public void addLongLocalAttr (int index, Object a){ addLocalAttr( index, a); } /** * this only returns the first attr of this type, there can be more * if you don't use client private types or the provided type is too general */ public <T> T getLocalAttr (int index, Class<T> attrType){ assert index < stackBase; if (attrs != null){ return ObjectList.getFirst( attrs[index], attrType); } return null; } public <T> T getNextLocalAttr (int index, Class<T> attrType, Object prev){ assert index < stackBase; if (attrs != null){ return ObjectList.getNext( attrs[index], attrType, prev); } return null; } public ObjectList.Iterator localAttrIterator(int index){ assert index < stackBase; Object a = (attrs != null) ? attrs[index] : null; return ObjectList.iterator(a); } public <T> ObjectList.TypedIterator<T> localAttrIterator(int index, Class<T> attrType){ assert index < stackBase; Object a = (attrs != null) ? attrs[index] : null; return ObjectList.typedIterator(a, attrType); } public void addLocalAttr (int index, Object attr){ assert index < stackBase; if (attrs == null){ if (attr == null) return; attrs = new Object[slots.length]; } attrs[index] = ObjectList.add(attrs[index], attr); } public void removeLocalAttr (int index, Object attr){ assert index < stackBase && attr != null; if (attr != null){ attrs[index] = ObjectList.remove(attrs[index], attr); } } public void replaceLocalAttr (int index, Object oldAttr, Object newAttr){ assert index < stackBase && oldAttr != null && newAttr != null; if (attrs == null){ attrs[index] = ObjectList.replace(attrs[index], oldAttr, newAttr); } } //--- various special attr accessors /** * helper to quickly find out if any of the locals slots holds * an attribute of the provided type * * @param attrType type of attribute to look for * @param startIdx local index to start from * @return index of local slot with attribute, -1 if none found */ public int getLocalAttrIndex (Class<?> attrType, int startIdx){ if (attrs != null){ for (int i=startIdx; i<stackBase; i++){ Object a = attrs[i]; if (ObjectList.containsType(a, attrType)){ return i; } } } return -1; } // <2do> this is machine dependent since it uses the operand stack. Only here because there // is no suitable place to factor this out between xStackFrame, xNativeStackFrame and xDirectCallStackFrame // (another example of missing multiple inheritance) // Needs to be overridden for Dalvik /** * this retrieves the argument values from the caller, i.e. the previous stackframe * * references are returned as ElementInfos or null * primitive values are returned as box objects (e.g. int -> Integer) */ public Object[] getArgumentValues (ThreadInfo ti){ StackFrame callerFrame = getCallerFrame(); if (callerFrame != null){ return callerFrame.getCallArguments(ti); } else { // <2do> what about main(String[] args) ? } return null; } /** * get the arguments of the executed call * Note - this throws an exception if the StackFrame pc is not an InvokeInstruction */ public Object[] getCallArguments (ThreadInfo ti){ if (pc == null || !(pc instanceof InvokeInstruction)){ throw new JPFException("stackframe not executing invoke: " + pc); } InvokeInstruction call = (InvokeInstruction) pc; MethodInfo callee = call.getInvokedMethod(); byte[] argTypes = callee.getArgumentTypes(); return getArgumentsValues(ti, argTypes); } public Object[] getArgumentsValues (ThreadInfo ti, byte[] argTypes){ int n = argTypes.length; Object[] args = new Object[n]; for (int i=n-1, off=0; i>=0; i--) { switch (argTypes[i]) { case Types.T_ARRAY: //case Types.T_OBJECT: case Types.T_REFERENCE: int ref = peek(off); if (ref != MJIEnv.NULL) { args[i] = ti.getElementInfo(ref); } else { args[i] = null; } off++; break; case Types.T_LONG: args[i] = new Long(peekLong(off)); off+=2; break; case Types.T_DOUBLE: args[i] = new Double(Types.longToDouble(peekLong(off))); off+=2; break; case Types.T_BOOLEAN: args[i] = new Boolean(peek(off) != 0); off++; break; case Types.T_BYTE: args[i] = new Byte((byte)peek(off)); off++; break; case Types.T_CHAR: args[i] = new Character((char)peek(off)); off++; break; case Types.T_SHORT: args[i] = new Short((short)peek(off)); off++; break; case Types.T_INT: args[i] = new Integer(peek(off)); off++; break; case Types.T_FLOAT: args[i] = new Float(Types.intToFloat(peek(off))); off++; break; default: // error, unknown argument type } } return args; } /** * return an array of all argument attrs, which in turn can be lists. If * you have to retrieve values, use the ObjectList APIs * * this is here (and not in ThreadInfo) because we might call it * on callerSlots cached/cloned StackFrame (caller stack might be already * modified, e.g. for callerSlots native method). * to be used from listeners. */ public Object[] getArgumentAttrs (MethodInfo miCallee) { if (attrs != null) { int nArgs = miCallee.getNumberOfArguments(); byte[] at = miCallee.getArgumentTypes(); Object[] a; if (!miCallee.isStatic()) { a = new Object[nArgs+1]; a[0] = getOperandAttr(miCallee.getArgumentsSize()-1); } else { a = new Object[nArgs]; } for (int i=nArgs-1, off=0, j=a.length-1; i>=0; i--, j--) { byte argType = at[i]; if (argType == Types.T_LONG || argType == Types.T_DOUBLE) { a[j] = getOperandAttr(off+1); off +=2; } else { a[j] = getOperandAttr(off); off++; } } return a; } else { return null; } } /** * check if there is any argument attr of the provided type on the operand stack * this is far more efficient than retrieving attribute values (we don't * care for argument types) */ public boolean hasArgumentAttr (MethodInfo miCallee, Class<?> attrType){ if (attrs != null) { int nArgSlots = miCallee.getArgumentsSize(); for (int i=0; i<nArgSlots; i++){ Object a = getOperandAttr(i); if (ObjectList.containsType(a, attrType)){ return true; } } } return false; } public boolean hasArgumentObjectAttr (ThreadInfo ti, MethodInfo miCallee, Class<?> type){ int nArgSlots = miCallee.getArgumentsSize(); for (int i=0; i<nArgSlots; i++){ if (isOperandRef(i)){ int objRef = peek(i); if (objRef != MJIEnv.NULL){ ElementInfo ei = ti.getElementInfo(objRef); if (ei.getObjectAttr(type) != null) { return true; } } } } return false; } // -- end attrs -- public void setLocalReferenceVariable (int index, int ref){ if (slots[index] != MJIEnv.NULL){ VM.getVM().getSystemState().activateGC(); } slots[index] = ref; isRef.set(index); } public void setLocalVariable (int index, int v){ // Hmm, should we treat this an error? if (isRef.get(index) && slots[index] != MJIEnv.NULL){ VM.getVM().getSystemState().activateGC(); } slots[index] = v; isRef.clear(index); } public void setFloatLocalVariable (int index, float f){ setLocalVariable( index, Float.floatToIntBits(f)); } public void setDoubleLocalVariable (int index, double f){ setLongLocalVariable( index, Double.doubleToLongBits(f)); } // <2do> replace with non-ref version public void setLocalVariable (int index, int v, boolean ref) { // <2do> activateGc should be replaced by local refChanged boolean activateGc = ref || (isRef.get(index) && (slots[index] != MJIEnv.NULL)); slots[index] = v; isRef.set(index,ref); if (activateGc) { VM.getVM().getSystemState().activateGC(); } } public int getLocalVariable (int i) { return slots[i]; } public int getLocalVariable (String name) { int idx = getLocalVariableSlotIndex(name); if (idx >= 0) { return getLocalVariable(idx); } else { throw new JPFException("local variable not found: " + name); } } public int getLocalVariableCount() { return stackBase; } /** * <2do> - this should return only LocalVarInfo for the current pc */ public LocalVarInfo[] getLocalVars () { return mi.getLocalVars(); } public boolean isLocalVariableRef (int idx) { return isRef.get(idx); } public String getLocalVariableType (String name) { LocalVarInfo lv = mi.getLocalVar(name, pc.getPosition()+pc.getLength()); if (lv != null){ return lv.getType(); } return null; } public String getLocalVariableType (int idx){ LocalVarInfo lv = mi.getLocalVar(idx, pc.getPosition()+pc.getLength()); if (lv != null){ return lv.getType(); } return null; } public LocalVarInfo getLocalVarInfo (String name){ return mi.getLocalVar(name, pc.getPosition()+pc.getLength()); } public LocalVarInfo getLocalVarInfo (int idx){ return mi.getLocalVar(idx, pc.getPosition()+pc.getLength()); } public void setThis (int objRef){ thisRef = objRef; } public FixedBitSet getReferenceMap(){ return isRef; } //--- direct slot access - provided for machine-independent clients public int[] getSlots () { return slots; // we should probably clone } public Object[] getSlotAttrs(){ return attrs; } public Object getSlotAttr (int i){ if (attrs != null){ return attrs[i]; } return null; } public <T> T getSlotAttr (int i, Class<T> attrType){ if (attrs != null){ return ObjectList.getFirst( attrs[i], attrType); } return null; } public void setSlotAttr (int i, Object a){ if (attrs == null){ attrs = new Object[slots.length]; } attrs[i] = a; } public void addSlotAttr (int i, Object a){ if (a != null){ if (attrs == null) { attrs = new Object[slots.length]; } attrs[i] = ObjectList.add(attrs[i], a); } } public void replaceSlotAttr (int i, Object oldAttr, Object newAttr){ if (attrs != null){ attrs[i] = ObjectList.replace(attrs[i], oldAttr, newAttr); } } public void visitReferenceSlots (ReferenceProcessor visitor){ for (int i=isRef.nextSetBit(0); i>=0 && i<=top; i=isRef.nextSetBit(i+1)){ visitor.processReference(slots[i]); } } public void setLongLocalVariable (int index, long v) { // WATCH OUT: apparently, slots can change type, so we have to // reset the reference flag (happened in JavaSeq) slots[index] = Types.hiLong(v); isRef.clear(index); index++; slots[index] = Types.loLong(v); isRef.clear(index); } public long getLongLocalVariable (int idx) { return Types.intsToLong(slots[idx + 1], slots[idx]); } public double getDoubleLocalVariable (int idx) { return Types.intsToDouble(slots[idx + 1], slots[idx]); } public float getFloatLocalVariable (int idx) { int bits = slots[idx]; return Float.intBitsToFloat(bits); } public double getDoubleLocalVariable (String name) { int idx = getLocalVariableSlotIndex(name); if (idx >= 0) { return getDoubleLocalVariable(idx); } else { throw new JPFException("long local variable not found: " + name); } } public long getLongLocalVariable (String name) { int idx = getLocalVariableSlotIndex(name); if (idx >= 0) { return getLongLocalVariable(idx); } else { throw new JPFException("long local variable not found: " + name); } } public MethodInfo getMethodInfo () { return mi; } public String getMethodName () { return mi.getName(); } public boolean isOperandRef (int offset) { return isRef.get(top-offset); } public boolean isOperandRef () { return isRef.get(top); } //--- direct pc modification // NOTE: this is dangerous, caller has to guarantee stack consistency public void setPC (Instruction newpc) { pc = newpc; } public Instruction getPC () { return pc; } public void advancePC() { int i = pc.getInstructionIndex() + 1; if (i < mi.getNumberOfInstructions()) { pc = mi.getInstruction(i); } else { pc = null; } } public int getTopPos() { return top; } ExceptionHandler getHandlerFor (ClassInfo ciException){ return mi.getHandlerFor (ciException, pc); } public boolean isFirewall (){ return mi.isFirewall(); } public String getStackTraceInfo () { StringBuilder sb = new StringBuilder(128); if(!mi.isJPFInternal()) { sb.append(mi.getStackTraceName()); if(pc != null) { sb.append('('); sb.append( pc.getFilePos()); sb.append(')'); } } else { sb.append(mi.getName()); if(mi.isMJI()) { sb.append("(Native)"); } else { sb.append("(Synthetic)"); } } return sb.toString(); } /** * if this is an instance method, return the reference of the corresponding object * (note this only has to be in slot 0 upon entry) */ public int getThis () { return thisRef; } // stack operations public void clearOperandStack () { if (attrs != null){ for (int i=stackBase; i<= top; i++){ attrs[i] = null; } } top = stackBase-1; } // this is callerSlots deep copy @Override public StackFrame clone () { try { StackFrame sf = (StackFrame) super.clone(); sf.defreeze(); sf.slots = slots.clone(); sf.isRef = isRef.clone(); if (attrs != null){ sf.attrs = attrs.clone(); } // frameAttr is not cloned to allow search global use return sf; } catch (CloneNotSupportedException cnsx) { throw new JPFException(cnsx); } } //--- change management protected void checkIsModifiable() { if ((attributes & ATTR_IS_FROZEN) != 0) { throw new JPFException("attempt to modify frozen stackframe: " + this); } } public void freeze() { attributes |= ATTR_IS_FROZEN; } public void defreeze() { attributes &= ~ATTR_IS_FROZEN; } public boolean isFrozen() { return ((attributes & ATTR_IS_FROZEN) != 0); } public void setReflection(){ attributes |= ATTR_IS_REFLECTION; } public boolean isReflection(){ return ((attributes & ATTR_IS_REFLECTION) != 0); } // all the dupses don't have any GC side effect (everything is already // on the stack), so skip the GC requests associated with push()/pop() public void dup () { // .. A => // .. A A // ^ int t= top; int td=t+1; slots[td] = slots[t]; isRef.set(td, isRef.get(t)); if (attrs != null){ attrs[td] = attrs[t]; } top = td; } public void dup2 () { // .. A B => // .. A B A B // ^ int ts, td; int t=top; // duplicate A td = t+1; ts = t-1; slots[td] = slots[ts]; isRef.set(td, isRef.get(ts)); if (attrs != null){ attrs[td] = attrs[ts]; } // duplicate B td++; ts=t; slots[td] = slots[ts]; isRef.set(td, isRef.get(ts)); if (attrs != null){ attrs[td] = attrs[ts]; } top = td; } public void dup2_x1 () { // .. A B C => // .. B C A B C // ^ int b, c; boolean bRef, cRef; Object bAnn = null, cAnn = null; int ts, td; int t = top; // duplicate C ts=t; td = t+2; // ts=top, td=top+2 slots[td] = c = slots[ts]; cRef = isRef.get(ts); isRef.set(td,cRef); if (attrs != null){ attrs[td] = cAnn = attrs[ts]; } // duplicate B ts--; td--; // ts=top-1, td=top+1 slots[td] = b = slots[ts]; bRef = isRef.get(ts); isRef.set(td, bRef); if (attrs != null){ attrs[td] = bAnn = attrs[ts]; } // shuffle A ts=t-2; td=t; // ts=top-2, td=top slots[td] = slots[ts]; isRef.set(td, isRef.get(ts)); if (attrs != null){ attrs[td] = attrs[ts]; } // shuffle B td = ts; // td=top-2 slots[td] = b; isRef.set(td, bRef); if (attrs != null){ attrs[td] = bAnn; } // shuffle C td++; // td=top-1 slots[td] = c; isRef.set(td, cRef); if (attrs != null){ attrs[td] = cAnn; } top += 2; } public void dup2_x2 () { // .. A B C D => // .. C D A B C D // ^ int c, d; boolean cRef, dRef; Object cAnn = null, dAnn = null; int ts, td; int t = top; // duplicate C ts = t-1; td = t+1; // ts=top-1, td=top+1 slots[td] = c = slots[ts]; cRef = isRef.get(ts); isRef.set(td, cRef); if (attrs != null){ attrs[td] = cAnn = attrs[ts]; } // duplicate D ts=t; td++; // ts=top, td=top+2 slots[td] = d = slots[ts]; dRef = isRef.get(ts); isRef.set(td, dRef); if (attrs != null){ attrs[td] = dAnn = attrs[ts]; } // shuffle A ts = t-3; td = t-1; // ts=top-3, td=top-1 slots[td] = slots[ts]; isRef.set( td, isRef.get(ts)); if (attrs != null){ attrs[td] = attrs[ts]; } // shuffle B ts++; td = t; // ts = top-2 slots[td] = slots[ts]; isRef.set( td, isRef.get(ts)); if (attrs != null){ attrs[td] = attrs[ts]; } // shuffle D td = ts; // td = top-2 slots[td] = d; isRef.set( td, dRef); if (attrs != null){ attrs[td] = dAnn; } // shuffle C td--; // td = top-3 slots[td] = c; isRef.set(td, cRef); if (attrs != null){ attrs[td] = cAnn; } top += 2; } public void dup_x1 () { // .. A B => // .. B A B // ^ int b; boolean bRef; Object bAnn = null; int ts, td; int t = top; // duplicate B ts = t; td = t+1; slots[td] = b = slots[ts]; bRef = isRef.get(ts); isRef.set(td, bRef); if (attrs != null){ attrs[td] = bAnn = attrs[ts]; } // shuffle A ts--; td = t; // ts=top-1, td = top slots[td] = slots[ts]; isRef.set( td, isRef.get(ts)); if (attrs != null){ attrs[td] = attrs[ts]; } // shuffle B td = ts; // td=top-1 slots[td] = b; isRef.set( td, bRef); if (attrs != null){ attrs[td] = bAnn; } top++; } public void dup_x2 () { // .. A B C => // .. C A B C // ^ int c; boolean cRef; Object cAnn = null; int ts, td; int t = top; // duplicate C ts = t; td = t+1; slots[td] = c = slots[ts]; cRef = isRef.get(ts); isRef.set( td, cRef); if (attrs != null){ attrs[td] = cAnn = attrs[ts]; } // shuffle B td = ts; ts--; // td=top, ts=top-1 slots[td] = slots[ts]; isRef.set( td, isRef.get(ts)); if (attrs != null){ attrs[td] = attrs[ts]; } // shuffle A td=ts; ts--; // td=top-1, ts=top-2 slots[td] = slots[ts]; isRef.set( td, isRef.get(ts)); if (attrs != null){ attrs[td] = attrs[ts]; } // shuffle C td = ts; // td = top-2 slots[td] = c; isRef.set(td, cRef); if (attrs != null){ attrs[td] = cAnn; } top++; } /** * to be used to check if a StackFrame got cloned due to its execution * changing attributes and/or slots, but otherwise represents the same * execution */ public boolean originatesFrom (StackFrame other){ if (other == this){ return true; } else { return ((mi == other.mi) && (prev == other.prev) && (top == other.top) && (getClass() == other.getClass())); } } // <2do> pcm - I assume this compares snapshots, not types. Otherwise it // would be pointless to equals stack/local values @Override public boolean equals (Object o) { if (o instanceof StackFrame){ StackFrame other = (StackFrame)o; if (prev != other.prev) { return false; } if (pc != other.pc) { return false; } if (mi != other.mi) { return false; } if (top != other.top){ return false; } int[] otherSlots = other.slots; FixedBitSet otherIsRef = other.isRef; for (int i=0; i<=top; i++){ if ( slots[i] != otherSlots[i]){ return false; } if ( isRef.get(i) != otherIsRef.get(i)){ return false; } } if (!Misc.compare(top,attrs,other.attrs)){ return false; } if (!ObjectList.equals(frameAttr, other.frameAttr)){ return false; } return true; } return false; } public boolean hasAnyRef () { return isRef.cardinality() > 0; } public int mixinExecutionStateHash (int h) { h = OATHash.hashMixin( h, mi.getGlobalId()); if (pc != null){ h = OATHash.hashMixin(h, pc.getInstructionIndex()); // we don't need the bytecode since there can only be one insn with this index in this method } for (int i=0; i<top; i++) { h = OATHash.hashMixin(h, slots[i]); } return h; } protected void hash (HashData hd) { if (prev != null){ hd.add(prev.objectHashCode()); } hd.add(mi.getGlobalId()); if (pc != null){ hd.add(pc.getInstructionIndex()); } for (int i=0; i<=top; i++){ hd.add(slots[i]); } isRef.hash(hd); // it's debatable if we add the attributes to the state, but whatever it // is, it should be kept consistent with the Fields.hash() if (attrs != null){ for (int i=0; i<=top; i++){ ObjectList.hash( attrs[i], hd); } } if (frameAttr != null){ ObjectList.hash(frameAttr, hd); } } // computes an hash code for the hash table // the default hash code is different for each object // we need to redifine it to make the hash table work @Override public int hashCode () { HashData hd = new HashData(); hash(hd); return hd.getValue(); } /** * mark all objects reachable from local or operand stack positions containing * references. Done during phase1 marking of threads (the stack is one of the * Thread gc roots) */ public void markThreadRoots (Heap heap, int tid) { /** for (int i = isRef.nextSetBit(0); i>=0 && i<=top; i = isRef.nextSetBit(i + 1)) { int objref = slots[i]; if (objref != MJIEnv.NULL) { heap.markThreadRoot(objref, tid); } } **/ for (int i = 0; i <= top; i++) { if (isRef.get(i)) { int objref = slots[i]; if (objref != MJIEnv.NULL) { heap.markThreadRoot(objref, tid); } } } } //--- debugging methods public void printOperands (PrintStream pw){ pw.print("operands = ["); for (int i=stackBase; i<=top; i++){ if (i>0){ pw.print(','); } if (isOperandRef(i)){ pw.print('^'); } pw.print(slots[i]); Object a = getOperandAttr(top-i); if (a != null){ pw.print(" {"); pw.print(a); pw.print('}'); } } pw.println(']'); } /** * this includes locals and pc */ public void printStackContent () { PrintStream pw = System.out; pw.print( "\tat "); pw.print( mi.getFullName()); if (pc != null) { pw.println( ":" + pc.getPosition()); } else { pw.println(); } pw.print("\t slots: "); for (int i=0; i<=top; i++){ if (i == stackBase){ pw.println("\t ----------- operand stack"); } pw.print( "\t ["); pw.print(i); pw.print("] "); if (isRef.get(i)) { pw.print( "@"); } pw.print( slots[i]); if (attrs != null){ pw.print(" attr="); pw.print(attrs[i]); } pw.println(); } } public void printStackTrace () { System.out.println( getStackTraceInfo()); } public void swap () { int t = top-1; int v = slots[top]; boolean isTopRef = isRef.get(top); slots[top] = slots[t]; isRef.set( top, isRef.get(t)); slots[t] = v; isRef.set( t, isTopRef); if (attrs != null){ Object a = attrs[top]; attrs[top] = attrs[t]; attrs[t] = a; } } protected void printContentsOn(PrintWriter pw){ pw.print("isFrozen="); pw.print(isFrozen()); pw.print(",mi="); pw.print( mi != null ? mi.getUniqueName() : "null"); pw.print(",top="); pw.print(top); pw.print(",slots=["); for (int i = 0; i <= top; i++) { if (i == stackBase){ pw.print("||"); } else { if (i != 0) { pw.print(','); } } if (isRef.get(i)){ pw.print('@'); } pw.print(slots[i]); if (attrs != null && attrs[i] != null) { pw.print('('); pw.print(attrs[i]); pw.print(')'); } } pw.print("],pc="); pw.print(pc != null ? pc.getPosition() : "null"); pw.print(']'); } // <2do> there are way too many different print/debug methods here public void printSlots (PrintStream ps){ for (int i = 0; i <= top; i++) { if (i == stackBase){ ps.print("||"); } else { if (i != 0) { ps.print(','); } } if (isRef.get(i)){ PrintUtils.printReference(ps, slots[i]); } else { ps.print(slots[i]); } } } public int getDepth(){ int depth = 0; for (StackFrame frame = prev; frame != null; frame = frame.prev){ depth++; } return depth; } protected int objectHashCode() { return super.hashCode(); } @Override public String toString () { StringWriter sw = new StringWriter(128); PrintWriter pw = new PrintWriter(sw); pw.print(getClass().getSimpleName() + '{'); //pw.print(Integer.toHexString(objectHashCode())); printContentsOn(pw); pw.print('}'); return sw.toString(); } public float peekFloat() { return Float.intBitsToFloat(slots[top]); } public float peekFloat (int offset){ return Float.intBitsToFloat(slots[top-offset]); } public double peekDouble() { int i = top; return Types.intsToDouble( slots[i], slots[i-1]); } public double peekDouble (int offset){ int i = top-offset; return Types.intsToDouble( slots[i], slots[i-1]); } public long peekLong () { int i = top; return Types.intsToLong( slots[i], slots[i-1]); } public long peekLong (int offset) { int i = top - offset; return Types.intsToLong( slots[i], slots[i-1]); } public void pushLong (long v) { push( (int) (v>>32)); push( (int) v); } public void pushDouble (double v) { long l = Double.doubleToLongBits(v); push( (int) (l>>32)); push( (int) l); } public void pushFloat (float v) { push( Float.floatToIntBits(v)); } public double popDouble () { int i = top; int lo = slots[i--]; int hi = slots[i--]; if (attrs != null){ i = top; attrs[i--] = null; // not really required attrs[i--] = null; // that's where the attribute should be } top = i; return Types.intsToDouble(lo, hi); } public long popLong () { int i = top; int lo = slots[i--]; int hi = slots[i--]; if (attrs != null){ i = top; attrs[i--] = null; // not really required attrs[i--] = null; // that's where the attribute should be } top = i; return Types.intsToLong(lo, hi); } public int peek () { return slots[top]; } public int peek (int offset) { return slots[top-offset]; } public void removeArguments (MethodInfo mi) { int i = mi.getArgumentsSize(); if (i != 0) { pop(i); } } public void pop (int n) { //assert (top >= stackBase) : "stack empty"; int t = top - n; // <2do> get rid of this ! for (int i=top; i>t; i--) { if (isRef.get(i) && (slots[i] != MJIEnv.NULL)) { VM.getVM().getSystemState().activateGC(); break; } } if (attrs != null){ // just to avoid memory leaks for (int i=top; i>t; i--){ attrs[i] = null; } } top = t; } public float popFloat() { int v = slots[top]; if (attrs != null){ // just to avoid memory leaks attrs[top] = null; } top--; return Float.intBitsToFloat(v); } public int pop () { //assert (top >= stackBase) : "stack empty"; int v = slots[top]; // <2do> get rid of this if (isRef.get(top)) { if (v != MJIEnv.NULL) { VM.getVM().getSystemState().activateGC(); } } if (attrs != null){ // just to avoid memory leaks attrs[top] = null; } top--; // note that we don't reset the operands or oRefs values, so that // we can still access them after the insn doing the pop got executed // (e.g. useful for listeners) return v; } public void pushLocal (int index) { top++; slots[top] = slots[index]; isRef.set(top, isRef.get(index)); if (attrs != null){ attrs[top] = attrs[index]; } } public void pushLongLocal (int index){ int t = top; slots[++t] = slots[index]; isRef.clear(t); slots[++t] = slots[index+1]; isRef.clear(t); if (attrs != null){ attrs[t-1] = attrs[index]; attrs[t] = null; } top = t; } public void storeOperand (int index){ slots[index] = slots[top]; isRef.set( index, isRef.get(top)); if (attrs != null){ attrs[index] = attrs[top]; attrs[top] = null; } top--; } public void storeLongOperand (int index){ int t = top-1; int i = index; slots[i] = slots[t]; isRef.clear(i); slots[++i] = slots[t+1]; isRef.clear(i); if (attrs != null){ attrs[index] = attrs[t]; // its in the lower word attrs[i] = null; attrs[t] = null; attrs[t+1] = null; } top -=2; } public void push (int v){ top++; slots[top] = v; isRef.clear(top); //if (attrs != null){ // done on pop // attrs[top] = null; //} } public void pushRef (int ref){ top++; slots[top] = ref; isRef.set(top); //if (attrs != null){ // done on pop // attrs[top] = null; //} if (ref != MJIEnv.NULL) { VM.getVM().getSystemState().activateGC(); } } public void push (int v, boolean ref) { top++; slots[top] = v; isRef.set(top, ref); //if (attrs != null){ // done on pop // attrs[top] = null; //} if (ref && (v != MJIEnv.NULL)) { VM.getVM().getSystemState().activateGC(); } } // return the value of callerSlots variable given the name public int getLocalVariableSlotIndex (String name) { LocalVarInfo lv = mi.getLocalVar(name, pc.getPosition()); if (lv != null){ return lv.getSlotIndex(); } return -1; } //--- abstract argument & return passing that can have VM dependend implementation public void setReferenceResult (int ref, Object attr){ pushRef(ref); if (attr != null){ setOperandAttr(attr); } } public void setResult (int r, Object attr){ push(r); if (attr != null){ setOperandAttr(attr); } } public void setResult (long r, Object attr){ pushLong(r); if (attr != null){ setLongOperandAttr(attr); } } public int getResult(){ return pop(); } public long getLongResult(){ return popLong(); } public int getReferenceResult () { return pop(); } public Object getResultAttr () { return getOperandAttr(); } public Object getLongResultAttr () { return getLongOperandAttr(); } public float getFloatResult(){ return Float.intBitsToFloat(getResult()); } public double getDoubleResult(){ return Double.longBitsToDouble(getLongResult()); } public Object getFloatResultAttr(){ return getResultAttr(); } public Object getDoubleResultAttr(){ return getLongResultAttr(); } //--- VM independent exception handler setup public void setExceptionReference (int exRef){ pushRef(exRef); } public int getExceptionReference (){ return pop(); } public void setExceptionReferenceAttribute (Object attr){ setOperandAttr(attr); } public Object getExceptionReferenceAttribute (){ return getOperandAttr(); } // those set the local vars that are normally initialized from call arguments public abstract void setArgumentLocal (int idx, int value, Object attr); public abstract void setLongArgumentLocal (int idx, long value, Object attr); public abstract void setReferenceArgumentLocal (int idx, int ref, Object attr); public void setFloatArgumentLocal (int idx, float value, Object attr){ setArgumentLocal( idx, Float.floatToIntBits(value), attr); } public void setDoubleArgumentLocal (int idx, double value, Object attr){ setLongArgumentLocal( idx, Double.doubleToLongBits(value), attr); } }