// // 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.vm; import java.io.PrintStream; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Iterator; import cmu.conditional.Conditional; import java.util.function.Function; import cmu.conditional.One; import de.fosd.typechef.featureexpr.FeatureExpr; import de.fosd.typechef.featureexpr.FeatureExprFactory; import gov.nasa.jpf.JPFException; import gov.nasa.jpf.jvm.bytecode.InvokeInstruction; 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.va.IStackHandler; import gov.nasa.jpf.vm.va.StackHandlerFactory; /** * 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 { protected FeatureExpr TRUE = FeatureExprFactory.True();// TODO remove /** * 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 Conditional<Integer> thisRef = One.MJIEnvNULL; // 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 Conditional<Instruction> pc; // the next insn to execute (program counter) protected MethodInfo mi; // which method is executed in this frame public IStackHandler stack; public int nLocals; /** * Buffer for the stack depth. */ private int depth = 0; protected int top() {// TODO remove return stack.getTop().getValue() + nLocals; } static final int[] EMPTY_ARRAY = new int[0]; static final FixedBitSet EMPTY_BITSET = new BitSet64(); protected StackFrame (FeatureExpr ctx, MethodInfo callee, int nLocals, int nOperands){ mi = callee; pc = new One<>(mi.getInstruction(0)); // ??? this.nLocals = nLocals; stackBase = nLocals; // int top() = nLocals-1; stack = StackHandlerFactory.createStack(ctx, nLocals, nOperands); // int nSlots = nLocals + nOperands; // if (nSlots > 0){ // slots = new int[nLocals + nOperands]; // isRef = createReferenceMap(stack.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( FeatureExprFactory.True(), callee, callee.getMaxLocals(), callee.getMaxStack()); } public StackFrame(FeatureExpr ctx, MethodInfo callee) { this( ctx, 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; this.nLocals = nLocals; stack = StackHandlerFactory.createStack(FeatureExprFactory.True(), nLocals, nOperands); } /** * re-execute method from the beginning - use with care */ public void reset() { pc = new One<>(mi.getInstruction(0)); // TODO ??? } public boolean isNative() { return false; } public StackFrame getCallerFrame() { MethodInfo callee = mi; for (StackFrame frame = getPrevious(); frame != null; frame = frame.getPrevious()) { Instruction insn = frame.getPC().getValue(); 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 Conditional<Integer> getCalleeThis (FeatureExpr ctx, MethodInfo mi) { return getCalleeThis(ctx, mi.getArgumentsSize()); } /** * return reference of called object in the context of the caller * (i.e. we are in the caller frame) */ public Conditional<Integer> getCalleeThis (FeatureExpr ctx, int size) { // top() is the topmost index int i = size-1; return stack.peek(ctx, i); // if (top() < i) { // return new One<>(-1); // } // return new One<>(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.getValue().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 = stack.getLocal(TRUE, slotIdx).getValue();//slots[slotIdx]; switch (sig.charAt(0)) { case 'Z': return Boolean.valueOf(v != 0); case 'B': return Byte.valueOf((byte) v); case 'C': return Character.valueOf((char) v); case 'S': return Short.valueOf((short) v); case 'I': return Integer.valueOf(v); case 'J': return Long.valueOf(Types.intsToLong(stack.getLocal(TRUE, slotIdx + 1).getValue(), v)); // Java is big endian, Types expects low,high case 'F': return Float.valueOf(Float.intBitsToFloat(v)); case 'D': return Double.valueOf(Double.longBitsToDouble(Types.intsToLong(stack.getLocal(TRUE, slotIdx + 1).getValue(), 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.getValue() != MJIEnv.NULL) { // it's an instance method ElementInfo ei = VM.getVM().getHeap().get(thisRef.getValue()); 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 (stack.isRefLocal(TRUE, j) && (stack.getLocal(TRUE, j).getValue().intValue() == 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 (stack.isRefLocal(TRUE, i) && (stack.getLocal(TRUE, i).getValue().intValue() == 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.getValue()); } /** * 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 (stack.isRefLocal(TRUE, i)){ visitor.processReference(stack.getLocal(TRUE, i).getValue()); } } } public int getSlot(int idx){ return stack.getLocal(TRUE, idx).getValue(); } public boolean isReferenceSlot(FeatureExpr ctx, int idx){ return stack.isRefLocal(ctx, idx); } public void setOperand (int offset, int v, boolean isRefValue){ // int i = top()-offset; // stack.get(i) = v; // isRef.set(i, isRefValue); stack.set(TRUE, offset, v, 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 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 (FeatureExpr ctx) { if (attrs != null && (getTopPos(ctx) >= stackBase)){ return attrs[getTopPos(ctx)]; } 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[stack.getLength()]; } 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[stack.getLength()]; } 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 (FeatureExpr ctx, int offset) { int i = getTopPos(ctx)-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[stack.getLength()]; } 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[stack.getLength()]; } 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 (FeatureExpr ctx) { return getOperandAttr(ctx, 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[stack.getLength()]; } 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[stack.getLength()]; } 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; } /** * 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.getValue() instanceof InvokeInstruction)){ throw new JPFException("stackframe not executing invoke: " + pc); } InvokeInstruction call = (InvokeInstruction) pc.getValue(); 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]; FeatureExpr ctx = null; 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(ctx, off).getValue(); if (ref != MJIEnv.NULL) { args[i] = ti.getElementInfo(ref); } else { args[i] = null; } off++; break; case Types.T_LONG: args[i] = new Long(peekLong(ctx, off).getValue()); off+=2; break; case Types.T_DOUBLE: args[i] = new Double(Types.longToDouble(peekLong(ctx, off).getValue())); off+=2; break; case Types.T_BOOLEAN: args[i] = new Boolean(peek(ctx, off).getValue().intValue() != 0); off++; break; case Types.T_BYTE: args[i] = new Byte((byte)peek(ctx, off).getValue().intValue()); off++; break; case Types.T_CHAR: args[i] = new Character((char)peek(ctx, off).getValue().intValue()); off++; break; case Types.T_SHORT: args[i] = new Short((short)peek(ctx, off).getValue().intValue()); off++; break; case Types.T_INT: args[i] = new Integer(peek(ctx, off).getValue().intValue()); off++; break; case Types.T_FLOAT: args[i] = new Float(Types.intToFloat(peek(ctx, off).getValue().intValue())); 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(TRUE, 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(TRUE, off+1); off +=2; } else { a[j] = getOperandAttr(TRUE, 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(TRUE, i); if (ObjectList.containsType(a, attrType)){ return true; } } } return false; } // -- end attrs -- public void setLocalReferenceVariable (FeatureExpr ctx, int index, int ref){ if (stack.getLocal(ctx, index).getValue().intValue() != MJIEnv.NULL){ VM.getVM().getSystemState().activateGC(); } stack.setLocal(ctx, index, ref, true); // stack.get(index) = ref; // isRef.set(index); } public void setLocalVariable (FeatureExpr ctx, int index, int v){ // Hmm, should we treat this an error? if (stack.isRefLocal(ctx, index) && stack.getLocal(ctx, index).getValue().intValue() != MJIEnv.NULL){ VM.getVM().getSystemState().activateGC(); } stack.setLocal(ctx, index, v, false); // stack.get(index) = v; // isRef.clear(index); } public void setFloatLocalVariable (FeatureExpr ctx, int index, float f){ setLocalVariable( ctx, index, Float.floatToIntBits(f)); } public void setDoubleLocalVariable (FeatureExpr ctx, int index, double f){ setLongLocalVariable(ctx, index, Double.doubleToLongBits(f)); } // <2do> replace with non-ref version public void setLocalVariable (FeatureExpr ctx, int index, Conditional<Integer> v, boolean ref) { // <2do> activateGc should be replaced by local refChanged boolean activateGc = ref || (stack.isRefLocal(ctx, index) && (stack.getLocal(ctx, index).getValue().intValue() != MJIEnv.NULL)); stack.setLocal(ctx, index, v, ref); // stack.get(index) = v; // isRef.set(index,ref); if (activateGc) { VM.getVM().getSystemState().activateGC(); } } public Conditional<Integer> getLocalVariable (FeatureExpr ctx, int i) { return stack.getLocal(ctx, i); } public int getLocalVariable (String name) {//JM UNUSED int idx = getLocalVariableSlotIndex(name); if (idx >= 0) { return getLocalVariable(TRUE, idx).getValue(); } 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 (FeatureExpr ctx, int idx) { return stack.isRefLocal(ctx, idx); } public String getLocalVariableType (String name) { LocalVarInfo lv = mi.getLocalVar(name, pc.getValue().getPosition()+pc.getValue().getLength()); if (lv != null){ return lv.getType(); } return null; } public String getLocalVariableType (int idx){ LocalVarInfo lv = mi.getLocalVar(idx, pc.getValue().getPosition()+pc.getValue().getLength()); if (lv != null){ return lv.getType(); } return null; } public LocalVarInfo getLocalVarInfo (String name){ return mi.getLocalVar(name, pc.getValue().getPosition()+pc.getValue().getLength()); } public LocalVarInfo getLocalVarInfo (int idx){ return mi.getLocalVar(idx, pc.getValue().getPosition()+pc.getValue().getLength()); } //--- use with extreme care - don't modify outside of StackFrame construction. THIS IS ONLY FOR INTERNAL USE /* * ths main reason for breaking encapsulation with these methods is that we need an API that allows efficient * initialization of slots from VM mode dependent invoke instructions. Argument passing differs between VMs */ public int[] getSlots () { return stack.getSlots(); // we should probably clone } // public FixedBitSet getReferenceMap(){ // throw new RuntimeException("Remove this call"); // } public Object[] getSlotAttrs(){ return attrs; } public Object getSlotAttr (int i){ if (attrs != null){ return attrs[i]; } return null; } public void setSlotAttr (int i, Object a){ if (attrs == null){ attrs = new Object[stack.getLength()]; } attrs[i] = a; } public void setThis (Conditional<Integer> thisRef2){ thisRef = thisRef2; } public void visitReferenceSlots (ReferenceProcessor visitor){ for (int i=0; i>=0 && i<=top(); i++){ if (stack.isRefLocal(TRUE, i)) { visitor.processReference(stack.getLocal(TRUE, i).getValue()); } } } public void setLongLocalVariable (FeatureExpr ctx, int index, long v) {//JM UNUSED // WATCH OUT: apparently, slots can change type, so we have to // reset the reference flag (happened in JavaSeq) stack.setLocal(ctx, index, Types.hiLong(v), false);// = Types.hiLong(v); // stack.setRef(index, false); // isRef.clear(index); index++; stack.setLocal(ctx, index, Types.loLong(v), false); // stack.setRef(index, false); // stack.get(index) = Types.loLong(v); // isRef.clear(index); } public long getLongLocalVariable (FeatureExpr ctx, int idx) { return Types.intsToLong(stack.getLocal(ctx, idx + 1).getValue(), stack.getLocal(ctx, idx).getValue()); } public double getDoubleLocalVariable (int idx) {//JM UNUSED return Types.intsToDouble(stack.getLocal(TRUE, idx + 1).getValue(), stack.getLocal(TRUE, idx).getValue()); } public float getFloatLocalVariable (int idx) {//JM UNUSED int bits = stack.getLocal(TRUE, idx).getValue(); 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(TRUE, 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 stack.isRef(TRUE, 0);//isRef.get(top()-offset); } public boolean isOperandRef () { return stack.isRef(TRUE, 0); } //--- direct pc modification // NOTE: this is dangerous, caller has to guarantee stack consistency public void setPC (Conditional<Instruction> newpc) { pc = newpc; } public Conditional<Instruction> getPC () { return pc; } public void advancePC() { int i = pc.getValue().getInstructionIndex() + 1; if (i < mi.getNumberOfInstructions()) { pc = new One<>(mi.getInstruction(i)); } else { pc = null; } } public int getTopPos() { return top(); } public int getTopPos(FeatureExpr ctx) { return stack.getTop().simplify(ctx).getValue() + nLocals; } ExceptionHandler getHandlerFor (FeatureExpr ctx, ClassInfo ciException){ return mi.getHandlerFor(ciException, pc.simplify(ctx).getValue()); } 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.getValue(true).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 Conditional<Integer> getThis () { return thisRef; } // stack operations public void clearOperandStack (FeatureExpr ctx) { if (attrs != null){ for (int i=stackBase; i<= top(); i++){ attrs[i] = null; } } stack.clear(ctx); } // this is callerSlots deep copy @Override public StackFrame clone () { try { StackFrame sf = (StackFrame) super.clone(); sf.defreeze(); sf.pc = pc.clone(); sf.stack = stack.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 (FeatureExpr ctx) { // .. A => // .. A A // ^ if (attrs != null){ attrs[top() + 1] = attrs[top()]; } stack.dup(ctx); } public void dup2 (FeatureExpr ctx) { // .. A B => // .. A B A B // ^ if (attrs != null){ attrs[top() + 1] = attrs[top() - 1]; } if (attrs != null){ attrs[top() + 2] = attrs[top()]; } stack.dup2(ctx); } public void dup2_x1 (FeatureExpr ctx) { // .. A B C => // .. B C A B C // ^ Object bAnn = null, cAnn = null; int ts, td; int t = attrs == null ? -1 : top(); stack.dup2_x1(ctx); ts=t; td = t+2; // ts=top, td=top+2 if (attrs != null){ attrs[td] = cAnn = attrs[ts]; } ts--; td--; // ts=top-1, td=top+1 if (attrs != null){ attrs[td] = bAnn = attrs[ts]; } // shuffle A ts=t-2; td=t; // ts=top-2, td=top if (attrs != null){ attrs[td] = attrs[ts]; } // shuffle B td = ts; // td=top-2 if (attrs != null){ attrs[td] = bAnn; } // shuffle C td++; // td=top-1 if (attrs != null){ attrs[td] = cAnn; } } public void dup2_x2 (FeatureExpr ctx) { // .. A B C D => // .. C D A B C D // ^ Object cAnn = null, dAnn = null; int ts, td; int t = top(); stack.dup2_x2(ctx); // duplicate C ts = t-1; td = t+1; // ts=top-1, td=top+1 if (attrs != null){ attrs[td] = cAnn = attrs[ts]; } // duplicate D ts=t; td++; // ts=top, td=top+2 if (attrs != null){ attrs[td] = dAnn = attrs[ts]; } // shuffle A ts = t-3; td = t-1; // ts=top-3, td=top-1 if (attrs != null){ attrs[td] = attrs[ts]; } // shuffle B ts++; td = t; // ts = top()-2 if (attrs != null){ attrs[td] = attrs[ts]; } // shuffle D td = ts; // td = top()-2 if (attrs != null){ attrs[td] = dAnn; } // shuffle C td--; // td = top()-3 if (attrs != null){ attrs[td] = cAnn; } } public void dup_x1 (FeatureExpr ctx) { // .. A B => // .. B A B // ^ Object bAnn = null; int ts, td; int t = 0; if (attrs != null){ t = top(); } stack.dup_x1(ctx); ts = t; td = t+1; if (attrs != null){ attrs[td] = bAnn = attrs[ts]; } ts--; td = t; if (attrs != null){ attrs[td] = attrs[ts]; } td = ts; if (attrs != null){ attrs[td] = bAnn; } } public void dup_x2 (FeatureExpr ctx) { // .. A B C => // .. C A B C // ^ stack.dup_x2(ctx); if (attrs != null) { Object cAnn = null; int ts, td; int t = top(); // duplicate C ts = t; td = t + 1; attrs[td] = cAnn = attrs[ts]; // shuffle B td = ts; ts--; // td=top, ts=top-1 attrs[td] = attrs[ts]; // shuffle A td = ts; ts--; // td=top-1, ts=top-2 attrs[td] = attrs[ts]; // shuffle C td = ts; // td = top()-2 attrs[td] = cAnn; } } // <2do> pcm - I assume this compares snapshots, not types. Otherwise it // would be pointless to compare 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 (getDepth() != other.getDepth()) { return false; } // if (top() != other.top()){ // return false; // } if (!other.stack.equals(stack)) { return false; } // int[] otherSlots = other.getSlots(); // FixedBitSet otherIsRef = other.isRef; // for (int i=0; i<=top(); i++){ // if ( stack.get(i) != otherstack.get(i)){ // return false; // } //// if ( stack.isRefIndex(i) != otherstack.isRefIndex(i)){ //// return false; //// } // } if (!Misc.compare(attrs,other.attrs)){ return false; } if (!ObjectList.equals(frameAttr, other.frameAttr)){ return false; } return true; } return false; } public boolean hasAnyRef (FeatureExpr ctx) { return stack.hasAnyRef(ctx);//isRef.cardinality() > 0; } public int mixinExecutionStateHash (int h) { h = OATHash.hashMixin( h, mi.getGlobalId()); if (pc != null){ h = OATHash.hashMixin(h, pc.getValue().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, stack.getLocal(TRUE, i).getValue()); } return h; } protected void hash (HashData hd) { if (prev != null){ hd.add(prev.objectHashCode()); } hd.add(mi.getGlobalId()); if (pc != null && pc.getValue(true) != null){ hd.add(pc.getValue(true).getInstructionIndex()); } // for (int i=0; i<=top(); i++){ // hd.add(stack.getLocal(TRUE, i).getValue(true)); // } // TODO ??? implement // int ls = isRef.longSize(); // for (int i=0; i<ls; i++){ // hd.add(isRef.getLong(i)); // } // 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 = stack.get(i); if (objref != MJIEnv.NULL) { heap.markThreadRoot(objref, tid); } } **/ // for (int i = 0; i <= top(); i++) { // if (stack.isRefLocal(TRUE, i)) { // int objref = stack.getLocal(TRUE, i).getValue(); // if (objref != MJIEnv.NULL) { // heap.markThreadRoot(objref, tid); // } // } // } for (Integer ref : stack.getAllReferences()) { if (ref != MJIEnv.NULL) { heap.markThreadRoot(ref, 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(stack.getLocal(TRUE, i).getValue()); Object a = getOperandAttr(TRUE, 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.getValue().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 (stack.isRefLocal(TRUE, i)) { pw.print( "@"); } pw.print( stack.getLocal(TRUE, i).getValue()); 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(); stack.swap(TRUE); if (attrs != null){ Object a = attrs[t]; attrs[t] = attrs[t - 1]; attrs[t - 1] = 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(stack.getTop()); pw.print(",slots=["); pw.print(stack); // for (int i = 0; i <= top(); i++) { // if (i == stackBase){ // pw.print("||"); // } else { // if (i != 0) { // pw.print(','); // } // } // // if (stack.isRefLocal(i)){ // pw.print('@'); // } // pw.print(stack.getLocal(TRUE, i).getValue()); // if (attrs != null && attrs[i] != null) { // pw.print('('); // pw.print(attrs[i]); // pw.print(')'); // } // } pw.print("],pc="); for (Instruction p : pc.toList()) { pw.print(p != null ? p.getPosition() + " " : "null"); } // pw.print(pc != null ? pc.getValue().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 (stack.isRefLocal(TRUE, i)){ PrintUtils.printReference(ps, stack.getLocal(TRUE, i).getValue()); } else { ps.print(stack.getLocal(TRUE, i).getValue()); } } } public int getDepth(){ if (depth != 0) { return depth; } if (prev == null) { return 0; } depth = prev.getDepth() + 1; 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 Conditional<Float> peekFloat(FeatureExpr ctx) { return stack.peekFloat(ctx, 0); } public double peekDouble(FeatureExpr ctx) { return peekDouble(ctx, 0); } public double peekDouble (FeatureExpr ctx, int offset){ return stack.peekDouble(ctx, offset).getValue(); } public Conditional<Long> peekLong (FeatureExpr ctx) { return peekLong(ctx, 0); } public Conditional<Long> peekLong (FeatureExpr ctx, int offset) { return stack.peekLong(ctx, offset); // return Types.intsToLong( stack.peek(ctx, offset).getValue(), stack.peek(ctx, offset + 1).getValue()); } // public void pushFloat (float v) { // stack.push(TRUE, v); // push(TRUE, new One<>(Float.floatToIntBits(v))); // } public Conditional<Double> popDouble (FeatureExpr ctx) { // int lo = stack.pop(ctx).getValue();//slots[i--]; // int hi = stack.pop(ctx).getValue();//slots[i--]; if (attrs != null){ int i = top(); attrs[i--] = null; // not really required attrs[i--] = null; // that's where the attribute should be } return stack.popDouble(ctx); // return Types.intsToDouble(lo, hi); } public Conditional<Long> popLong (FeatureExpr ctx) { // int lo = stack.pop(TRUE).getValue();//get(i--);//slots[i--]; // int hi = stack.pop(TRUE).getValue();////slots[i--]; if (attrs != null){ int i = top(); // i = top(); attrs[i--] = null; // not really required attrs[i--] = null; // that's where the attribute should be } // stack.incrTop(-2);//top() = i; // return Types.intsToLong(lo, hi); return stack.popLong(ctx); } public Conditional<Integer> peek (FeatureExpr ctx) { return stack.peek(ctx);//new One<>(slots[top()]); } public Conditional<Integer> peek (FeatureExpr ctx, int offset) { return stack.peek(ctx, offset);//new One<>(slots[top-offset]); } public void removeArguments (FeatureExpr ctx, MethodInfo mi) { int i = mi.getArgumentsSize(); if (i != 0) { pop(ctx, i); } } public void pop (FeatureExpr ctx, int n) { // assert (top() >= stackBase) : "stack empty"; // <2do> get rid of this ! // for (int i=top(); i>t; i--) { // if (stack.isRefIndex(i) && (stack.get(i) != MJIEnv.NULL)) { // VM.getVM().getSystemState().activateGC(); // break; // } // } if (hasAttrs()) { int top = getTopPos(ctx); int t = top - n; if (attrs != null) { // just to avoid memory leaks for (int i = top; i > t; i--) { attrs[i] = null; } } } stack.pop(ctx, n); } public Conditional<Float> popFloat(FeatureExpr ctx) { // int v = stack.pop(TRUE).getValue();//stack.peek();//stack.peek(); if (attrs != null){ // just to avoid memory leaks attrs[top()] = null; } return stack.popFloat(ctx); // return Float.intBitsToFloat(v); } public Conditional<Integer> pop (FeatureExpr ctx) { if (ctx == null) { ctx = FeatureExprFactory.True(); } //assert (top() >= stackBase) : "stack empty"; boolean isRef = stack.isRef(ctx, 0); Conditional<Integer> v = stack.pop(ctx); // <2do> get rid of this if (isRef) { if (v instanceof One && v.getValue() != MJIEnv.NULL) { VM.getVM().getSystemState().activateGC(); } } if (attrs != null) { // just to avoid memory leaks attrs[top() + 1] = 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 (FeatureExpr ctx, int index) { stack.pushLocal(ctx, index); // stack.incrTop(ctx, 1); // stack.duplicateIndex(ctx, index, 0, true); // top++; // slots[top()] = stack.get(index); // isRef.set(top, stack.isRefIndex(index)); if (attrs != null){ attrs[top()] = attrs[index]; } } public void pushLongLocal (FeatureExpr ctx, int index){ stack.pushLongLocal(ctx, index); // stack.pushLocal(ctx, index); // stack.pushLocal(ctx, index + 1); // stack.duplicateIndex(ctx, index, 1, true); // stack.duplicateIndex(ctx, index+1, 2, true); // stack.incrTop(ctx, 2); // // slots[++t] = stack.get(index); // isRef.clear(t); // slots[++t] = slots[index+1]; // isRef.clear(t); if (attrs != null){ int t = top(); attrs[t-1] = attrs[index]; attrs[t] = null; } // top() = t; } public void storeOperand (FeatureExpr ctx, int index){ if (attrs != null){ int top = getTopPos(ctx); attrs[index] = attrs[top]; attrs[top] = null; } stack.storeOperand(ctx, index); // stack.duplicateIndex(ctx, index, 0, false); // stack[index] = stack.peek(); // isRef.set( index, stack.isRef(0)); // stack.incrTop(ctx, -1); // top()--; } public void storeLongOperand (FeatureExpr ctx, int index){ // int i = index; // stack.storeOperand(ctx, index + 1); // stack.storeOperand(ctx, index); stack.storeLongOperand(ctx, index); // stack.duplicateIndex(ctx, index, -1, false); // stack.setRef(index, false); // stack.get(i) = slots[t]; // isRef.clear(i); // stack.duplicateIndex(ctx, index + 1, 0, false); // stack.setRef(index + 1, false); // slots[++i] = slots[t+1]; // isRef.clear(i); if (attrs != null){ int t = top()+1; attrs[index] = attrs[t]; // its in the lower word attrs[index + 1] = null; attrs[t] = null; attrs[t+1] = null; } // stack.incrTop(ctx, -2); // top() -=2; } public <T> void push (FeatureExpr ctx, Conditional<T> v, boolean ref){ stack.push(ctx, v, ref); } public <T> void push (FeatureExpr ctx, Conditional<T> v){ stack.push(ctx, v, false); // top++; // slots[top()] = v.getValue(); // isRef.clear(top); //if (attrs != null){ // done on pop // attrs[top()] = null; //} } public void pushRef (FeatureExpr ctx, int ref) { // quick fix if (ctx == null) { ctx = FeatureExprFactory.True(); } pushRef(ctx, new One<>(ref)); } public void pushRef(FeatureExpr ctx, Conditional<Integer> ref) { stack.push(ctx, ref, true); if (ref instanceof One && ref.getValue() != MJIEnv.NULL) { VM.getVM().getSystemState().activateGC(); } } public void push (FeatureExpr ctx, int v, boolean ref) { stack.push(ctx, v, 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.getValue().getPosition()); if (lv != null){ return lv.getSlotIndex(); } return -1; } //--- abstract argument & return passing that is shared between VM types public void setReferenceResult (int ref, Object attr){ pushRef(TRUE, ref); if (attr != null){ setOperandAttr(attr); } } public void setResult (int r, Object attr){ push(TRUE, new One<>(r)); if (attr != null){ setOperandAttr(attr); } } public void setResult (long r, Object attr){ push(TRUE, new One<>(r)); if (attr != null){ setLongOperandAttr(attr); } } public int getResult(){ return pop(TRUE).getValue(); } public long getLongResult(){ return popLong(TRUE).getValue(); } public int getReferenceResult (FeatureExpr ctx) { return pop(ctx).getValue(); } public Object getResultAttr () { return getOperandAttr(TRUE); } public Object getLongResultAttr () { return getOperandAttr(TRUE); } 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, FeatureExpr ctx){ pushRef(ctx, exRef); } public int getExceptionReference (){ return pop(TRUE).getValue(); } public void setExceptionReferenceAttribute (Object attr){ setOperandAttr(attr); } public Object getExceptionReferenceAttribute (){ return getOperandAttr(TRUE); } // 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); } public void IINC(FeatureExpr ctx, int index, int increment) { stack.IINC(ctx, index, increment); } /** * Returns the current stack trace as String. */ public String trace(FeatureExpr ctx) { if (prev == null) { return mi.getFullName(); } final Conditional<Integer> lineNumber = pc.simplify(ctx).map(new Function<Instruction, Integer>() { @Override public Integer apply(Instruction instruction) { return instruction.getLineNumber(); } }); return mi.getFullName() + "("+ lineNumber + ")\n" + prev.trace(ctx); } }