| /* VMIdManager.java -- A reference/example implementation of a manager for | |
| JDWP object/reference type IDs | |
| Copyright (C) 2005, 2006 Free Software Foundation | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath is distributed in the hope that it will be useful, but | |
| WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package gnu.classpath.jdwp; | |
| import gnu.classpath.jdwp.exception.InvalidClassException; | |
| import gnu.classpath.jdwp.exception.InvalidObjectException; | |
| import gnu.classpath.jdwp.id.*; | |
| import java.lang.ref.Reference; | |
| import java.lang.ref.ReferenceQueue; | |
| import java.lang.ref.SoftReference; | |
| import java.nio.ByteBuffer; | |
| import java.util.HashMap; | |
| import java.util.Hashtable; | |
| /** | |
| * This class manages objects and referencetypes that are reported | |
| * to the debugger. All objects and referencetypes reported to the | |
| * debugger should go through this manager. | |
| * | |
| * A brief summary of what an <code>IdManager</code> must provide: | |
| * | |
| * <code> | |
| * public ObjectId getObjectId (Object theObject); | |
| * public ObjectId get (long id); | |
| * public ObjectId readObjectId (ByteBuffer bb); | |
| * public ReferenceTypeId getReferenceTypeId (Class clazz); | |
| * public ReferenceTypeId getReferenceType (long id); | |
| * public ReferenceTypeId readReferenceTypeId (ByteBuffer bb); | |
| * </code> | |
| * | |
| * See the javadoc on these methods later in this file for more | |
| * information on these functions. | |
| * | |
| * <b>NOTE:</b> All IDs handled by the ID manager (all object and reference | |
| * type IDs) are assumed to be of type <code>long</code>. | |
| * | |
| * <b>NOTE:</b> This class does not manage virtual machine-specific types, | |
| * like methods, fields, and frames. These already have unique IDs within | |
| * the virtual machine and do not need further abstraction here. | |
| * | |
| * @author Keith Seitz (keiths@redhat.com) | |
| */ | |
| public class VMIdManager | |
| { | |
| // This factory generates ids for objects and types that may | |
| // be sent to a debugger. | |
| private static class IdFactory | |
| { | |
| // ID of last object / referencetype | |
| private static Object _idLock = new Object (); | |
| private static Object _ridLock = new Object (); | |
| private static long _lastId = 0; | |
| private static long _lastRid = 0; | |
| // A list of all ID types | |
| private static HashMap _idList = new HashMap (); | |
| // Initialize the id list with known types | |
| static | |
| { | |
| // ObjectId and ArrayId are special cases. See newObjectId. | |
| _idList.put (ClassLoaderId.typeClass, ClassLoaderId.class); | |
| _idList.put (ClassObjectId.typeClass, ClassObjectId.class); | |
| _idList.put (StringId.typeClass, StringId.class); | |
| _idList.put (ThreadId.typeClass, ThreadId.class); | |
| _idList.put (ThreadGroupId.typeClass, ThreadGroupId.class); | |
| } | |
| /** | |
| * Returns a new id for the given object | |
| * | |
| * @param obj SoftReference of the object for which an id is desired | |
| * @returns a suitable object id | |
| */ | |
| public static ObjectId newObjectId (SoftReference obj) | |
| { | |
| ObjectId id = null; | |
| Object object = obj.get (); | |
| // Special case: arrays | |
| if (object.getClass ().isArray ()) | |
| id = new ArrayId (); | |
| else | |
| { | |
| // Loop through all classes until we hit baseclass | |
| Class myClass; | |
| for (myClass = object.getClass (); myClass != null; | |
| myClass = myClass.getSuperclass ()) | |
| { | |
| Class clz = (Class) _idList.get (myClass); | |
| if (clz != null) | |
| { | |
| try | |
| { | |
| id = (ObjectId) clz.newInstance (); | |
| synchronized (_idLock) | |
| { | |
| id.setId (++_lastId); | |
| } | |
| id.setReference (obj); | |
| return id; | |
| } | |
| catch (InstantiationException ie) | |
| { | |
| // This really should not happen | |
| throw new RuntimeException ("cannot create new ID", ie); | |
| } | |
| catch (IllegalAccessException iae) | |
| { | |
| // This really should not happen | |
| throw new RuntimeException ("illegal access of ID", iae); | |
| } | |
| } | |
| } | |
| /* getSuperclass returned null and no matching ID type found. | |
| So it must derive from Object. */ | |
| id = new ObjectId (); | |
| } | |
| synchronized (_idLock) | |
| { | |
| id.setId (++_lastId); | |
| } | |
| id.setReference (obj); | |
| return id; | |
| } | |
| /** | |
| * Returns a new reference type id for the given class | |
| * | |
| * @param ref SoftReference to the desired type | |
| * @returns a suitable reference type id or null when the | |
| * reference is cleared. | |
| */ | |
| public static ReferenceTypeId newReferenceTypeId (SoftReference ref) | |
| { | |
| ReferenceTypeId id; | |
| Class clazz = (Class) ref.get (); | |
| if (clazz == null) | |
| return null; | |
| if (clazz.isArray ()) | |
| id = new ArrayReferenceTypeId (); | |
| else if (clazz.isInterface ()) | |
| id = new InterfaceReferenceTypeId (); | |
| else | |
| id = new ClassReferenceTypeId (); | |
| id.setReference (ref); | |
| synchronized (_ridLock) | |
| { | |
| id.setId (++_lastRid); | |
| } | |
| return id; | |
| } | |
| } | |
| /** | |
| * This class is a SoftReferenceIdentity type that is used by | |
| * the ID manager. | |
| */ | |
| class ReferenceKey extends SoftReference | |
| { | |
| // Hash code of referent | |
| private int _hash; | |
| /** | |
| * Constructs a new <code>ReferenceKey</code> object | |
| * with the given referent. | |
| * | |
| * <p>This constructor should only be used for object lookups | |
| * by the backend. | |
| * | |
| * @param referent the object to reference | |
| */ | |
| public ReferenceKey (Object referent) | |
| { | |
| super (referent); | |
| _hash = referent.hashCode (); | |
| } | |
| /** | |
| * Constructs a new <code>ReferenceKey</code> object | |
| * with the given referent and reference queue. | |
| * | |
| * <p>The JDWP back-end stores a <code>ReferenceKey</code> | |
| * with its corresponding <code>JdwpId</code>. This constructor | |
| * is used by the back-end when adding new IDs to be managed. | |
| * | |
| * @param referent the object to reference | |
| * @param queue the queue to which to report garbage collections | |
| */ | |
| public ReferenceKey (Object referent, ReferenceQueue queue) | |
| { | |
| super (referent, queue); | |
| _hash = referent.hashCode (); | |
| } | |
| /** | |
| * Returns the hash code of the referent. | |
| * This seems hacky, but is required in order to use this class | |
| * as a hash table key. | |
| * | |
| * @returns the hash code of the referent | |
| */ | |
| public int hashCode () | |
| { | |
| return _hash; | |
| } | |
| /** | |
| * Comparator for keys | |
| * | |
| * This method can be used in two ways: | |
| * | |
| * <ol> | |
| * <li>For table lookups, where we want to compare referents</li> | |
| * <li>For clearing GCd objects, where we want to compare the actual | |
| * key object (not the referent)</li> | |
| * </ol> | |
| */ | |
| public boolean equals (Object obj) | |
| { | |
| if (obj instanceof ReferenceKey) | |
| { | |
| ReferenceKey ref = (ReferenceKey) obj; | |
| /* First check if the two references are the same. | |
| If they are, that means we must be clearing GCd objects. */ | |
| if (this == obj) | |
| return true; | |
| return (ref.get () == get ()); | |
| } | |
| return false; | |
| } | |
| } | |
| // instance of VMIdManager | |
| private static VMIdManager _idm = new VMIdManager (); | |
| // A reference queue for our objects | |
| private ReferenceQueue _refQueue; | |
| // Mapping of objects (ReferenceKey) to IDs (ObjectId) | |
| private Hashtable _oidTable; | |
| // Mapping of ID numbers (Long) to IDs (ObjectId) | |
| private Hashtable _idTable; | |
| /* Mapping of class (ReferenceKey) to IDs (ReferenceTypeId) for reference | |
| types. Unlike other types, reference id types are NEVER released. */ | |
| private Hashtable _classTable; | |
| // Mapping of ID numbers (Long) to reference type IDs (ReferenceTypeId) | |
| private Hashtable _ridTable; | |
| /** | |
| * Gets the instance of VMIdManager, constructing a new one | |
| * if none currently exists. | |
| */ | |
| public static VMIdManager getDefault () | |
| { | |
| return _idm; | |
| } | |
| // Constructs a new <code>IdManager</code> | |
| private VMIdManager () | |
| { | |
| _refQueue = new ReferenceQueue (); | |
| _oidTable = new Hashtable (50); | |
| _idTable = new Hashtable (50); | |
| _classTable = new Hashtable (20); | |
| _ridTable = new Hashtable (20); | |
| } | |
| // Updates the object ID table, removing IDs whose objects have | |
| // been garbage collected. | |
| private void _update () | |
| { | |
| Reference ref; | |
| while ((ref = _refQueue.poll ()) != null) | |
| { | |
| ObjectId id = (ObjectId) _oidTable.get (ref); | |
| _oidTable.remove (ref); | |
| _idTable.remove (new Long (id.getId ())); | |
| } | |
| } | |
| /** | |
| * Returns an id for the given object, adding it | |
| * if it does not have an id. | |
| * | |
| * @param theObject the object to get an ID/add | |
| * @returns the ID of the object | |
| */ | |
| public ObjectId getObjectId (Object theObject) | |
| { | |
| ReferenceKey ref = new ReferenceKey (theObject, _refQueue); | |
| ObjectId id = (ObjectId) _oidTable.get (ref); | |
| if (id == null) | |
| { | |
| // update the tables -- this is an arbitrary place to put this | |
| _update (); | |
| // Object not found. Make new id for it | |
| id = IdFactory.newObjectId (ref); | |
| _oidTable.put (ref, id); | |
| _idTable.put (new Long (id.getId ()), id); | |
| } | |
| return id; | |
| } | |
| /** | |
| * Returns the <code>JdwpId</code> for a given ID. Unlike | |
| * <code>getId</code>, it throws an exception if the ID is not | |
| * known. | |
| * | |
| * @param id the numerical ID of the desired <code>JdwpId</code> | |
| * @throws InvalidObjectException if the ID is not found | |
| */ | |
| public ObjectId get (long id) | |
| throws InvalidObjectException | |
| { | |
| ObjectId oid = (ObjectId) _idTable.get (new Long (id)); | |
| if (oid == null) | |
| throw new InvalidObjectException (id); | |
| return oid; | |
| } | |
| public ObjectId readObjectId (ByteBuffer bb) | |
| throws InvalidObjectException | |
| { | |
| long id = bb.getLong (); | |
| return get (id); | |
| } | |
| /** | |
| * Gets the reference type id for the given class, creating | |
| * a new one if it does not already have an id | |
| * | |
| * @param clazz the class for which to get an ID | |
| * @returns the ID of the class | |
| */ | |
| public ReferenceTypeId getReferenceTypeId (Class clazz) | |
| { | |
| ReferenceKey ref = new ReferenceKey (clazz); | |
| ReferenceTypeId id = (ReferenceTypeId)_classTable.get (ref); | |
| if (id == null) | |
| { | |
| // Object not found. Make new id for it | |
| id = IdFactory.newReferenceTypeId (ref); | |
| _classTable.put (ref, id); | |
| _ridTable.put (new Long (id.getId ()), id); | |
| } | |
| return id; | |
| } | |
| /** | |
| * Returns the <code>ReferenceTypeId</code> for a given ID. Unlike | |
| * <code>getReferenceTypeId</code>, it throws an exception if the ID is not | |
| * known. | |
| * | |
| * @param id the numerical ID of the desired reference type | |
| * @throws InvalidClassException if the ID is not found | |
| */ | |
| public ReferenceTypeId getReferenceType (long id) | |
| throws InvalidClassException | |
| { | |
| ReferenceTypeId rid = (ReferenceTypeId) _ridTable.get (new Long (id)); | |
| if (rid == null) | |
| throw new InvalidClassException (id); | |
| return rid; | |
| } | |
| public ReferenceTypeId readReferenceTypeId (ByteBuffer bb) | |
| throws InvalidClassException | |
| { | |
| long id = bb.getLong (); | |
| return getReferenceType (id); | |
| } | |
| } |