package lejos.nxt;
import java.util.Iterator;
/**
* This class provides access to many of the internal structures of the leJOS
* virtual machine. In particular it provides Java level access to the classes,
* methods fields etc. that make up the currently executing program. These
* structures are used by the VM to create the in memory program. They are
* similar to the class file format used by a standard JVM, but with much of the
* detail stripped away.
*
* The structures fall into two genral types. Those that are contained within
* Java objects (and so can be made directly available to a user program) and
* those that are simply raw data. To allow access to the second type of data
* we create Java objects with a copy of the data in them. In some cases we
* also do this even for tables/etc. that are available as objects as wrapping
* the class in this way may make it easier to manipulate.
*
* NOTE: Many of the structures in this file are a direct mapping to internal
* data within the VM. Changes to the VM structures must also be reflected here.
* Take care when editing this code to ensure that changes do not modify this
* mapping. Also many of the tables use byte fields that really should be
* treated as unsigned values. We have to take great care when converting these
* to integers.
* @author andy
*/
public final class VM
{
//The peek methods use the follow base address when accessing VM memory.
private static final int ABSOLUTE = 0;
private static final int THREADS = 1;
private static final int HEAP = 2;
private static final int IMAGE = 3;
private static final int STATICS = 4;
private static final int MEM = 5;
// Offsets and masks to allow access to a standard Object header
private static final int OBJ_HDR_SZ = 4;
private static final int OBJ_FLAGS = 1;
private static final int OBJ_LEN_MASK = 0x3f;
private static final int OBJ_LEN_OBJECT = 0x3f;
private static final int OBJ_LEN_BIGARRAY = 0x3e;
private static final int OBJ_CLASS = 0;
private static final int OBJ_BIGARRAY_LEN = 4;
// Basic variable types used within the VM
public static final int VM_OBJECT = 0;
public static final int VM_CLASS = 2;
public static final int VM_BOOLEAN = 4;
public static final int VM_CHAR = 5;
public static final int VM_FLOAT = 6;
public static final int VM_DOUBLE = 7;
public static final int VM_BYTE = 8;
public static final int VM_SHORT = 9;
public static final int VM_INT = 10;
public static final int VM_LONG = 11;
public static final int VM_VOID = 12;
public static final int VM_OBJECTARRAY = 13;
// The base address of the in memory program header
private static final int IMAGE_BASE = memPeekInt(MEM, IMAGE*4);
// Provide access to the image header structure
private static final int IMAGE_HDR_LEN = 20;
private static final int LAST_CLASS_OFFSET = 17;
private static final int LAST_CLASS = memPeekByte(IMAGE, LAST_CLASS_OFFSET);
private int METHOD_BASE;
private static final int METHOD_OFFSET = 4;
private static VM theVM;
// Singleton don't allow new from other classes
private VM()
{
//IMAGE_BASE = memPeekInt(MEM, IMAGE*4);
image = new VMImage(IMAGE_BASE);
METHOD_BASE = memPeekShort(IMAGE, IMAGE_HDR_LEN+METHOD_OFFSET) + IMAGE_BASE;
}
/**
* Obtain access to the single instance of the VM class. This can then be
* used to gain access to the more detailed information about the VM and
* it's internal structures.
* @return the VM object
*/
public static VM getVM()
{
if (theVM == null)
theVM = new VM();
return theVM;
}
// Low level memory access functions
/**
* Return up to 4 bytes from a specified memory location.
* @param base Base section of memory.
* @param offset Offset (in bytes) of the location
* @param typ The primitive data type to access
* @return Memory location contents.
*/
private static native int memPeek(int base, int offset, int typ);
/**
* Copy the specified number of bytes from memory into the given object.
* @param value Object to copy to
* @param objoffset Offset (in bytes) within the object
* @param base Base section to copy from
* @param offset Offset within the section
* @param len Number of bytes to copy
*/
private static native void memCopy(Object obj, int objoffset, int base, int offset, int len);
/**
* Return the address of the given objects first data field.
* @param value
* @return the required address
*/
private native static int getDataAddress (Object obj);
/**
* Return the address of the given object.
* @param value
* @return the required address
*/
private native static int getObjectAddress(Object obj);
/**
* Return a Java object reference the points to the location provided.
* @param base Memory section that offset refers to.
* @param offset The offset from the base in bytes.
* @return
*/
private native static Object memGetReference(int base, int offset);
/**
* Return a single byte from the specified memory location.
* @param base
* @param offset
* @return byte value from memory
*/
private static int memPeekByte(int base, int offset)
{
return memPeek(base, offset, VM_BYTE) & 0xff;
}
/**
* Return a 16 bit word from the specified memory location.
* @param base
* @param offset
* @return short value from memory
*/
private static int memPeekShort(int base, int offset)
{
return memPeek(base, offset, VM_SHORT) & 0xffff;
}
/**
* Return a 32 bit word from the specified memory location.
* @param base
* @param offset
* @return int value from memory
*/
private static int memPeekInt(int base, int offset)
{
return memPeek(base, offset, VM_INT);
}
// The flash structure for class data has a special header added to it to
// allow it to be treated as a Java object. This is not the normal 4 byte
// object header (because the second two bytes are used for locking which
// would not work for a flash based object), it is a two byte header. We
// need to allow for this header when moving around.
private static final int CLASS_OBJ_HDR = 2;
// The tables and the class objects are aligned on 4 byte boundaries. Other
// items are aligned on 2 or 1 byte boundaries.
private static final int TABLE_ALIGNMENT = 4;
private static final int CLASS_ALIGNMENT = 4;
private static final int METHOD_ALIGNMENT = 2;
private static final int EXCEPTION_ALIGNMENT = 2;
private static final int FIELD_ALIGNMENT = 1;
private static final int STATIC_FIELD_ALIGNMENT = 2;
private static final int CONSTANT_ALIGNMENT = 2;
/**
* This class is used to create a Java class from in memory data. This data
* may not have a Java object header or may be of a different class to that
* desired. The new object will contain a snapshot of the in memory data.
* This snapshot can be updated (if required by calling the update() method.
*/
public static class VMClone
{
// Offset of the first cloned data field within the Java object. This
// is after any red tape fields like the length and address. We also need
// to allow for the extra "this" value.
private static final int CLONE_OFFSET = 8;
private final int length;
final int address;
public void update()
{
memCopy(this, CLONE_OFFSET, ABSOLUTE, address, length);
}
private VMClone(int addr, int len)
{
address = addr;
length = len;
update();
}
}
/**
* Class that represents a value within the VM. The type field indicates the
* basic type of the value. The value object is used to return the actual
* contents. For primitive types we return a boxed type, for objects we
* simply return the object.
* value.
*/
public static class VMValue
{
public final int type;
public final Object value;
// Number of bytes for a basic type
private static final int[] lengths = {4, 0, 1, 0, 1, 2, 4, 8, 1, 2, 4, 8};
/**
* Create the value object based upon an address.
* @param typ The basic type of the value
* @param addr The absolute address of the value
*/
private VMValue(int typ, int addr)
{
type = typ;
switch(typ)
{
case VM_OBJECT:
value = memGetReference(ABSOLUTE, memPeekInt(ABSOLUTE, addr));
break;
case VM_INT:
value = new Integer(memPeekInt(ABSOLUTE, addr));
break;
case VM_BYTE:
value = new Byte((byte)memPeekByte(ABSOLUTE, addr));
break;
case VM_CHAR:
value = new Character((char)memPeekShort(ABSOLUTE, addr));
break;
case VM_SHORT:
value = new Short((short)memPeekShort(ABSOLUTE, addr));
break;
case VM_LONG:
value = new Long(((long) memPeekInt(ABSOLUTE, addr) << 32) | (long)memPeekInt(ABSOLUTE, addr+4));
break;
case VM_FLOAT:
value = new Float(Float.intBitsToFloat(memPeekInt(ABSOLUTE, addr)));
break;
case VM_DOUBLE:
value = new Double(Double.longBitsToDouble(((long)memPeekInt(ABSOLUTE, addr) << 32) | (long)memPeekInt(ABSOLUTE, addr+4)));
break;
case VM_BOOLEAN:
value = new Boolean(memPeekByte(ABSOLUTE, addr) != 0);
break;
case VM_CLASS:
value = VM.getVM().getVMClass(memPeekByte(ABSOLUTE, addr));
break;
default:
throw new NoSuchFieldError();
}
}
/**
* Create a value object based upon a Java object.
* @param obj The actual object value.
*/
private VMValue(Object obj)
{
type = VM_OBJECT;
this.value = obj;
}
}
/**
* The image header for the currently active program.
*/
public final class VMImage extends VMClone
{
public short magicNumber;
public short constantTableOffset;
public short constantValuesOffset;
public short numConstants;
public short staticFieldsOffset;
public short staticStateLength;
public short numStaticFields;
public short entryClassesOffset;
public byte numEntryClasses;
public byte lastClass;
public short runtimeOptions;
private VMImage(int addr)
{
super(addr, IMAGE_HDR_LEN);
}
/**
* Return an object that can be used to access all of the available class
* structures.
* @return Class access object
*/
public VMClasses getVMClasses()
{
return new VMClasses(lastClass + 1);
}
/**
* Return an object that can be used to access all of the available
* constant values.
* @return Constant access object
*/
public VMConstants getVMConstants()
{
return new VMConstants(constantTableOffset + IMAGE_BASE, numConstants);
}
/**
* Return an object that can be used to access all of the static fields.
* @return Field access object
*/
public VMStaticFields getVMStaticFields()
{
return new VMStaticFields(staticFieldsOffset+IMAGE_BASE, numStaticFields);
}
/**
* Get the base address for the current image, useful when converting
* real address to relative ones.
* @return the base address for the current image.
*/
public int getImageBase()
{
return IMAGE_BASE;
}
}
// Cached version of the image header.
private final VMImage image;
/**
* Return the image header for the currently running program
* @return Image header.
*/
public VMImage getImage()
{
return image;
}
/**
* This class provides the ability to iterate through a series of in memory
* structures and returns a Java accessible clone of the structure.
* @param <E>
*/
public static abstract class VMItems<E> implements Iterable<E>
{
int cnt;
private class VMItemsIterator implements Iterator<E>
{
int next = 0;
public boolean hasNext()
{
return (next < cnt);
}
public E next()
{
return get(next++);
}
public void remove()
{
throw new UnsupportedOperationException("Not supported yet.");
}
}
public Iterator<E> iterator()
{
return new VMItemsIterator();
}
abstract public E get(int entry);
private VMItems(int cnt)
{
this.cnt = cnt;
}
}
// Provide access to the static field data.
private static final int FIELD_LEN = 2;
/**
* This class can be used to gain access to all of the static fields.
*/
public static final class VMStaticFields extends VMItems<VMValue>
{
private final int baseAddr;
private final int dataAddr;
/**
* Return a VMValue object for the specified static field number
* @param item
* @return VMValue for this item
*/
@Override
public VMValue get(int item)
{
if (item >= cnt) throw new NoSuchFieldError();
int addr = baseAddr + item*((FIELD_LEN + STATIC_FIELD_ALIGNMENT - 1) & ~(STATIC_FIELD_ALIGNMENT -1));
int rec = memPeekShort(ABSOLUTE, addr);
int typ = (rec >> 12) & 0xf;
int offset = rec & 0xfff;
return new VMValue(typ, dataAddr+offset);
}
private VMStaticFields(int base, int cnt)
{
super(cnt);
baseAddr = base;
dataAddr = memPeekInt(MEM, STATICS*4);
}
}
// Provide access to constant values
private static final int CONSTANT_LEN = 4;
/**
* This class allows access to all of the constant values.
*/
public final class VMConstants extends VMItems<VMValue>
{
private final int baseAddr;
/**
* Return a VMValue object for the specified constant table entry.
* @param item
* @return VMValue object for the constant.
*/
@Override
public VMValue get(int item)
{
if (item >= cnt) throw new NoSuchFieldError();
int addr = baseAddr + item*((CONSTANT_LEN + CONSTANT_ALIGNMENT - 1) & ~(CONSTANT_ALIGNMENT -1));
int offset = memPeekShort(ABSOLUTE, addr) + IMAGE_BASE;
int typ = memPeekByte(ABSOLUTE, addr+2);
int len = memPeekByte(ABSOLUTE, addr+3);
if (typ == VM_OBJECT)
{
// Must be a string constant
char chars[] = new char[len];
for(int i = 0; i < len; i++)
chars[i] = (char)memPeekByte(ABSOLUTE, offset+i);
return new VMValue(new String(chars));
}
len = VMValue.lengths[typ];
return new VMValue(typ, offset);
}
private VMConstants(int base, int cnt)
{
super(cnt);
baseAddr = base;
}
}
// Provide access to internal exception data
private static final int EXCEPTION_LEN = 7;
/**
* An exception record
*/
public static final class VMException extends VMClone
{
public short start;
public short end;
public short handler;
public byte classIndex;
private VMException(int addr)
{
super(addr, EXCEPTION_LEN);
}
}
/**
* Class to provide access to a series of exception records
*/
public static final class VMExceptions extends VMItems<VMException>
{
private final int baseAddr;
private VMExceptions(int baseAddr, int cnt)
{
super(cnt);
this.baseAddr = baseAddr;
}
@Override
public VMException get(int item)
{
return new VMException(baseAddr + item*((EXCEPTION_LEN + EXCEPTION_ALIGNMENT - 1)&~(EXCEPTION_ALIGNMENT - 1)));
}
}
// Provide access to internal method data
private static final int METHOD_LEN = 11;
/**
* Provide access to information about a method
*/
public final class VMMethod extends VMClone
{
public short signature;
public short exceptionTable;
public short codeOffset;
public byte numLocals;
public byte maxOperands;
public byte numParameters;
public byte numExceptionHandlers;
public byte mflags;
// Flag values
public static final byte M_NATIVE = 1;
public static final byte M_SYNCHRONIZED = 2;
public static final byte M_STATIC = 4;
private VMMethod(int addr)
{
super(addr, METHOD_LEN);
}
/**
* Return access to the exception records for this method.
* @return the VMExceptions object
*/
public VMExceptions getVMExceptions()
{
return new VMExceptions(exceptionTable + IMAGE_BASE, numExceptionHandlers);
}
public int getMethodNumber()
{
return (address - METHOD_BASE)/((METHOD_LEN + METHOD_ALIGNMENT - 1) & ~(METHOD_ALIGNMENT - 1));
}
}
/**
* Provide access to a series of method records
*/
public final class VMMethods extends VMItems<VMMethod>
{
private final int baseAddr;
private VMMethods(int baseAddr, int cnt)
{
super(cnt);
this.baseAddr = baseAddr;
}
/**
* Return access to a specific method.
* @param item
* @return the VMMethod object
*/
@Override
public VMMethod get(int item)
{
return new VMMethod(baseAddr + item*((METHOD_LEN + METHOD_ALIGNMENT - 1) & ~(METHOD_ALIGNMENT - 1)));
}
}
// Provide access to the internal class data
// This is the size of data within the structure it does not include
// the special object header. We need to add the size of the special header
// when moving from item to item.
private static final int CLASS_LEN = 10;
/**
* Provide access to the internal class data
*/
public final class VMClass extends VMClone
{
public short size;
public short CIAData1;
public short CIAData2;
public byte CIACnt1;
public byte CIACnt2;
public byte parentClass;
public byte flags;
// Class flags
public static final byte C_ARRAY = 2;
public static final byte C_HASCLINIT = 4;
public static final byte C_INTERFACE = 8;
public static final byte C_NOREFS = 0x10;
public static final byte C_PRIMITIVE = 0x20;
// The following are not part of the internal structure
private int clsNo;
private VMClass(int addr)
{
super(addr+CLASS_OBJ_HDR, CLASS_LEN);
clsNo = (addr - IMAGE_BASE - IMAGE_HDR_LEN)/((CLASS_LEN+CLASS_OBJ_HDR + CLASS_ALIGNMENT - 1) & ~(CLASS_ALIGNMENT - 1));
}
/**
* Return access to the methods for this class
* @return the VMMethods object
*/
public VMMethods getMethods()
{
// Interfaces and arrays to not have method tables.
if ((flags & (C_ARRAY|C_INTERFACE)) != 0)
return new VMMethods(0, 0);
else
return new VMMethods(CIAData1 + IMAGE_BASE, ((int)CIACnt1 & 0xff));
}
/**
* Return the class number of this class
* @return the class number
*/
public int getClassNo()
{
return clsNo;
}
/**
* Return a Java Class object for this class.
* @return Java Class object
*/
public Class<?> getJavaClass()
{
//return classFactory.makeRef(getClassAddress(clsNo));
return (Class<?>) memGetReference(ABSOLUTE, getClassAddress(clsNo));
}
}
/**
* Provide access to a series of class records
*/
public final class VMClasses extends VMItems<VMClass>
{
private VMClasses(int cnt)
{
super(cnt);
}
/**
* return a specific class object
* @param item
* @return the VMClass object
*/
@Override
public VMClass get(int item)
{
if (item >= cnt) throw new NoClassDefFoundError();
return new VMClass(getClassAddress(item));
}
}
public final class VMFields extends VMItems<VMValue>
{
private final int fieldOffsets[];
private final byte fieldTypes[];
// Keep a reference to make sure the object does not get gc'ed
private final Object obj;
private VMFields(Object obj)
{
super(0);
VMClass cls = getVMClass(obj);
if (isArray(obj))
{
fieldOffsets = new int[0];
fieldTypes = new byte[0];
}
else
{
// first work out how many fields there are (including those
// from super classes.
cnt = ((int)cls.CIACnt2 & 0xff);
while (cls.parentClass != 0)
{
cls = getVMClass(cls.parentClass);
cnt += ((int)cls.CIACnt2 & 0xff);
}
// Now create a type and offset map. Note that we only have type
// data so we have to start at the object end and work back
// from there.
fieldOffsets = new int[cnt];
fieldTypes = new byte[cnt];
cls = getVMClass(obj);
int fieldBase = getObjectAddress(obj);
int offset = fieldBase + cls.size;
int item = cnt-1;
for(;;)
{
int fieldTable = cls.CIAData2 + IMAGE_BASE;
for(int i = ((int)cls.CIACnt2 & 0xff) - 1; i >= 0; i--)
{
fieldTypes[item] = (byte)memPeekByte(ABSOLUTE, fieldTable + i);
offset -= VMValue.lengths[fieldTypes[item]];
fieldOffsets[item--] = offset;
}
if (cls.parentClass == 0) break;
cls = getVMClass(cls.parentClass);
}
}
this.obj = obj;
}
/**
* Return a specified field
* @param item The required field number
* @return A value object to access the field.
*/
@Override
public VMValue get(int item)
{
if (item >= cnt) throw new NoSuchFieldError();
return new VMValue(fieldTypes[item], fieldOffsets[item]);
}
}
public static final class VMElements extends VMItems<VMValue>
{
private final int arrayBase;
private final int typ;
private final Object obj;
VMElements(Object obj)
{
super(0);
if (obj == null) throw new NullPointerException();
int addr = getObjectAddress(obj);
int len = memPeekByte(ABSOLUTE, addr+OBJ_FLAGS) & OBJ_LEN_MASK;
if (len == OBJ_LEN_OBJECT)
{
typ = -1;
arrayBase = 0;
}
else
{
int cls = memPeekByte(ABSOLUTE, addr+OBJ_CLASS);
if (cnt >= OBJ_LEN_BIGARRAY)
cnt = memPeekByte(ABSOLUTE, addr+OBJ_BIGARRAY_LEN);
else
cnt = len;
arrayBase = getDataAddress(obj);
// Convert class into basic type, anything other than a primitive
// is an object.
if (cls >= (VM_OBJECTARRAY+VM_BOOLEAN) && cls <= (VM_OBJECTARRAY + VM_LONG))
typ = cls - VM_OBJECTARRAY;
else
typ = VM_OBJECT;
}
this.obj = obj;
}
@Override
public VMValue get(int item)
{
if (item >= cnt) throw new ArrayIndexOutOfBoundsException();
int offset = arrayBase + item*VMValue.lengths[typ];
return new VMValue(typ, offset);
}
public int length()
{
return cnt;
}
}
// NOTE: Some of the following methods should really be in VMClasses, but
// it just makes things a little cleaner to access them if they are here!
/**
* Return the address of the specified class number
* @param clsNo
* @return the address of the object
*/
private static int getClassAddress(int clsNo)
{
return IMAGE_BASE + IMAGE_HDR_LEN + (clsNo & 0xff)*((CLASS_LEN+CLASS_OBJ_HDR + CLASS_ALIGNMENT - 1) & ~(CLASS_ALIGNMENT - 1));
}
/**
* Return the class number of the class for the specified object
* @param value Object to obtain the class number for.
* @return The requested class number
*/
private int getClassNo(Object obj)
{
if (obj == null) throw new NullPointerException();
// First we get the address of the object
int addr = getObjectAddress(obj);
int cls = memPeekByte(ABSOLUTE, addr+OBJ_CLASS);
return cls;
}
public static int getClassNumber(Class<?> cls)
{
int addr = getObjectAddress(cls);
return (addr - IMAGE_BASE - IMAGE_HDR_LEN)/((CLASS_LEN+CLASS_OBJ_HDR + + CLASS_ALIGNMENT-1) & ~(CLASS_ALIGNMENT-1));
}
/**
* Check to see if it is allowed to assign an object of class src to an
* object of class dst.
* @param src The src class number
* @param dst The destination class number
* @return true if the assignment is allowed.
*/
private static native boolean isAssignable(int src, int dst);
/**
* Check to see if it is allowed to assign an object of class src to an
* oobject of class dst.
* @param src The src class.
* @param dst The destination class.
* @return true if the assignment is allowed.
* @exception NullPointerException if either class is null.
*/
public static boolean isAssignable(Class<?> src, Class<?> dst)
{
if (src == null || dst == null) throw new NullPointerException();
int srcNo = getClassNumber(src);
int dstNo = getClassNumber(dst);
return isAssignable(srcNo, dstNo);
}
/**
* Return the Class object for the provided object.
* Note: The actual object returned actually resides in flash rom and is
* part of the leJOS loader. It is not possible to extend this class or
* modify the contents.
* @param obj the object for which the class is required
* @return the Class object
*/
public Class<?> getClass(Object obj)
{
//return classFactory.makeRef(getClassAddress(getClassNo(value)));
return (Class<?>) memGetReference(ABSOLUTE, getClassAddress(getClassNo(obj)));
}
/**
* Return a VMClass object for the provided object.
* Note: The object returned is actually a copy of the in flash object.
* @param obj the object for which the class is required
* @return the VMClass object
*/
public VMClass getVMClass(Object obj)
{
return new VMClass(getClassAddress(getClassNo(obj)));
}
/**
* Return the class for the specified primitive type.
* @param clsNo
* @return Class object for this type.
*/
public static Class<?> getClass(int clsNo)
{
if (clsNo > LAST_CLASS) return null;
return (Class<?>) memGetReference(ABSOLUTE, getClassAddress(clsNo));
}
/**
* Return a VMClass object for the provided class object.
* Note: The object returned is actually a copy of the in flash object.
* @param cls
* @return the VMClass object
*/
public final VMClass getVMClass(Class<?> cls)
{
return new VMClass(getObjectAddress(cls));
}
/**
* Return a VMClass object for the provided class number.
* Note: The object returned is actually a copy of the in flash object.
* @param clsNo
* @return the VMClass object
*/
public final VMClass getVMClass(int clsNo)
{
if (clsNo > LAST_CLASS) return null;
return new VMClass(getClassAddress(clsNo));
}
/**
* Return data about the specified method number
* @param methodNo
* @return Method object
*/
public final VMMethod getMethod(int methodNo)
{
return new VMMethod(METHOD_BASE + methodNo*((METHOD_LEN + 1) & ~1));
}
/**
* Return true if the specified object is an array
* @param obj object to test
* @return true iff the specified object is an array
*/
public final boolean isArray(Object obj)
{
if (obj == null) return false;
return (memPeekByte(ABSOLUTE, getObjectAddress(obj)+OBJ_FLAGS) & OBJ_LEN_MASK) != OBJ_LEN_OBJECT;
}
/**
* Provide access to the fields of an object
* @param obj the object to obtain the fields for
* @return fields object
*/
public final VMFields getFields(Object obj)
{
return new VMFields(obj);
}
public final VMElements getElements(Object obj)
{
return new VMElements(obj);
}
private static final int STACKFRAME_LEN = 20;
public final class VMStackFrame extends VMClone
{
public int methodRecord;
public Object monitor;
public int localsBase;
public int pc;
public int stackTop;
private VMStackFrame(int addr)
{
super(addr, STACKFRAME_LEN);
}
public VMMethod getVMMethod()
{
return new VMMethod(methodRecord);
}
}
public final class VMStackFrames extends VMItems<VMStackFrame>
{
private final int base;
private final int size;
private VMStackFrames(Object stackFrame, int size)
{
super(size);
base = getDataAddress(stackFrame);
this.size = size;
}
@Override
public VMStackFrame get(int item)
{
int offset = base + (size - item) * STACKFRAME_LEN;
return new VMStackFrame(offset);
}
}
// Provide access to the internal Thread data.
private static final int THREAD_LEN = 31;
/**
* Internal version of a thread structure
*/
public final class VMThread extends VMClone
{
public Thread nextThread;
public Object waitingOn;
public int sync;
public int sleepUntil;
public Object stackFrameArray;
public Object stackArray;
public byte stackFrameArraySize;
public byte monitorCount;
public byte threadId;
public byte state;
public byte priority;
public byte interrupted;
public byte daemon;
// The following is not part of the VM internal structure
private final Thread thread;
private VMThread(int addr)
{
super(addr, THREAD_LEN);
thread = (Thread)memGetReference(ABSOLUTE, addr - OBJ_HDR_SZ);
}
/**
* Return a Java Thread object for this thread.
* @return Java Thread object
*/
public Thread getJavaThread()
{
return thread;
}
public VMStackFrames getStackFrames()
{
return new VMStackFrames(stackFrameArray, stackFrameArraySize);
}
public VMStackFrames getStackFrames(int frameCnt)
{
return new VMStackFrames(stackFrameArray, frameCnt);
}
}
/**
* Provide access to a series of internal thread records
*/
public final class VMThreads implements Iterable<VMThread>
{
private class VMThreadIterator implements Iterator<VMThread>
{
private int nextPriority = Thread.MAX_PRIORITY-1;
private int first = 0;
private int nextThread = 0;
private void findNext()
{
if (nextThread != 0)
nextThread = memPeekInt(ABSOLUTE, nextThread + OBJ_HDR_SZ);
if (nextThread == first)
{
first = 0;
while (nextPriority >= 0 && first == 0)
{
first = memPeekInt(THREADS, nextPriority*4);
nextPriority--;
}
nextThread = first;
}
}
public boolean hasNext()
{
return nextThread != 0;
}
public VMThread next()
{
VMThread ret = new VMThread(nextThread+OBJ_HDR_SZ);
findNext();
return ret;
}
public void remove()
{
throw new UnsupportedOperationException("Not supported.");
}
VMThreadIterator()
{
findNext();
}
}
public Iterator<VMThread> iterator()
{
return new VMThreadIterator();
}
private VMThreads()
{
// Nothing
}
}
/**
* Returns access to all of the current internal thread objects
* @return the VMThreads object
*/
public final VMThreads getVMThreads()
{
return new VMThreads();
}
public final VMThread getVMThread(Thread thread)
{
return new VMThread(getDataAddress(thread));
}
/**
* Suspend a thread. This places the specified thread into a suspended
* state. If thread is null all threads except for the current thread will
* be suspended.
* @param thread
*/
public native static final void suspendThread(Object thread);
/**
* Resume a thread. A suspended thread will be resumed to it's previous
* state. If thread is null all suspended threads will be resumed.
* @param thread
*/
public native static final void resumeThread(Object thread);
/**
* leJOS allows several "programs" to be linked into a single nxj file
* the system by default will start execution of program 0. This function
* allows other programs to be called.
* @param progNo program number to call
*/
public native static final void executeProgram(int progNo);
// Flags used to control the Virtual Machine.
public static final int VM_TYPECHECKS = 1;
public static final int VM_ASSERT = 2;
/**
* Control the run time operation of the leJOS Virtual Machine.
* @param options Bit flags.
*/
public static final native void setVMOptions(int options);
/**
* Return the currently operating Virtual Machine options.
* @return the options
*/
public static final native int getVMOptions();
/**
* Enable/Disable strict run time type checking for some operations within
* the Virtual Machine.
* @param on
*/
public static void enableRunTimeTypeChecks(boolean on)
{
int cur = getVMOptions();
if (on)
cur |= VM_TYPECHECKS;
else
cur &= ~VM_TYPECHECKS;
setVMOptions(cur);
}
}