mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			436 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			436 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Java
		
	
	
	
/* VMIdManager.java -- A reference/example implementation of a manager for
 | 
						|
   JDWP object/reference type IDs
 | 
						|
 | 
						|
   Copyright (C) 2005, 2006, 2007 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)
 | 
						|
  {
 | 
						|
    // Special case: null object.
 | 
						|
    if (theObject == null)
 | 
						|
      return new NullObjectId ();
 | 
						|
	  
 | 
						|
    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
 | 
						|
  {
 | 
						|
    // Special case: null object id.
 | 
						|
    if (id == 0)
 | 
						|
      return new NullObjectId ();
 | 
						|
    
 | 
						|
    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);
 | 
						|
  }
 | 
						|
}
 |