/*
 * (C) Copyright IBM Corp. 2001, 2003 VM_Interface.java: methods that JMTk
 * requires to interface with its enclosing run-time environment. OVM version
 * take 1
 */
//$Id: VM_Interface.java,v 1.20 2005/09/26 05:01:50 dholmes Exp $

package s3.core.services.memory.vmInterface;

import java.lang.ref.Reference;

import ovm.core.OVMBase;
import ovm.core.domain.DomainDirectory;
import ovm.core.domain.ObjectModel;
import ovm.core.domain.Oop;
import ovm.core.execution.Activation;
import ovm.core.execution.Context;
import ovm.core.execution.CoreServicesAccess;
import ovm.core.execution.Native;
import ovm.core.execution.NativeConstants;
import ovm.core.services.memory.JMTkOop;
import ovm.core.services.memory.MemoryManager;
import ovm.core.services.memory.MovingGC;
import ovm.core.services.memory.VM_Address;
import ovm.core.services.memory.VM_Word;
import ovm.services.bytecode.JVMConstants.Throwables;
import ovm.util.Mem;
import s3.core.domain.S3Blueprint;
import s3.core.services.memory.LocalReferenceIterator;
import s3.core.services.memory.SpaceScanner;
import s3.core.services.memory.JMTk.AddressDeque;
import s3.core.services.memory.JMTk.AddressPairDeque;
import s3.core.services.memory.JMTk.Plan;
import s3.core.services.memory.JMTk.UnimplementedException;
import s3.core.services.memory.JMTk.VM_PragmaInline;
import s3.core.services.memory.JMTk.VM_PragmaInterruptible;
import s3.core.services.memory.JMTk.VM_PragmaLogicallyUninterruptible;
import s3.core.services.memory.JMTk.VM_PragmaNoInline;
import s3.core.services.memory.JMTk.VM_PragmaUninterruptible;
import s3.core.services.memory.JMTk.VM_Uninterruptible;
import s3.core.services.memory.JMTk.VMResource;
import s3.services.bootimage.BootImage;
import s3.util.PragmaNoInline;
import s3.util.PragmaTransformCallsiteIR.BCdead;

/**
 * The interface that the Jikes research virtual machine presents to the JMTk
 * memory manager.
 * @author Perry Cheng
 * @version $Revision: 1.20 $ @date $Date: 2005/09/26 05:01:50 $
 */

public class VM_Interface implements VM_Uninterruptible {

    /***************************************************************************
     * Class variables
     */

    /**
     * The address of the start of the boot image.
     */
  public static final VM_Address bootImageAddress =
      VM_Address.fromInt(MemoryManager.the().getImageBaseAddress());

    /**
     * The address in virtual memory that is the highest that can be mapped.
     */
  public static VM_Address MAXIMUM_MAPPABLE = VM_Address.fromInt(0x80000000);

    /**
     * An unknown GC trigger reason. Signals a logic bug.
     */
    public static final int UNKNOWN_GC_TRIGGER = 0;
    /**
     * Externally triggered garbage collection. For example, the application
     * called System.gc().
     */
    public static final int EXTERNAL_GC_TRIGGER = 1;
    /**
     * Resource triggered garbage collection. For example, an allocation
     * request would take the number of pages in use beyond the number
     * available.
     */
    public static final int RESOURCE_GC_TRIGGER = 2;
    /**
     * Internally triggered garbage collection. For example, the memory manager
     * attempting another collection after the first failed to free space.
     */
    public static final int INTERNAL_GC_TRIGGER = 3;
    /**
     * The number of garbage collection trigger reasons.
     */
    public static final int TRIGGER_REASONS = 4;
    /**
     * Short descriptions of the garbage collection trigger reasons.
     */
    private static final String[] triggerReasons =
        {
            "unknown",
            "external request",
            "resource exhaustion",
            "internal request" };

    /**
     * <code>true</code> if assertions should be verified
     */
    public static final boolean VerifyAssertions = true;

    /**
     * The percentage threshold for throwing an OutOfMemoryError. If, after a
     * garbage collection, the amount of memory used as a percentage of the
     * available heap memory exceeds this percentage the memory manager will
     * throw an OutOfMemoryError.
     */
    public static final double OUT_OF_MEMORY_THRESHOLD = 0.98;

    /**
     * Counter to track index into thread table for root tracing.
     */
    //private static SynchronizedCounter threadCounter = new
    // SynchronizedCounter();

    /**
     * The fully qualified name of the collector thread.
     */
    //private static VM_Atom collectorThreadAtom;
    /**
     * The string "run".
     */
    //private static VM_Atom runAtom;

    /**
     * An enumerator used to forward root objects
     */
    //private static PreCopyEnumerator preCopyEnum;

    /**
     * OVM only runs on one kernel thread, so there will only be one plan
     * instance. This is returned whenever JMTk asks for the plan for the
     * current processor
     */
    private static Plan plan;

    /***************************************************************************
     * Initialization
     */

    /**
     * Initialization that occurs at <i>build</i> time. The values of statics
     * at the completion of this routine will be reflected in the boot image.
     * Any objects referenced by those statics will be transitively included in
     * the boot image.
     * <p>
     * This is all well and good, but as long as BasePlan.<clinit>
     * depends on VM_Interface, we need to defer allocation of the
     * plan until BasePlan is initialized
     */
  static{
	// plan = new Plan();
  }

    /***************************************************************************
     * What we need to know about memory allocated by the outside world
     * <p>
     * Basically where the boot image is, and how much memory is available.
     */

    /**
     * Returns the start of the boot image.
     * @return the address of the start of the boot image
     */
    public static VM_Address bootImageStart() throws VM_PragmaUninterruptible {
        return Native.getImageBaseAddress();
    }

    /**
     * Return the end of the boot image.
     * @return the address of the end of the boot image
     */
    public static VM_Address bootImageEnd() throws VM_PragmaUninterruptible {
        return Native.getImageEndAddress();
    }

    /***************************************************************************
     * Manipulate raw memory
     */

    /**
     * Maps an area of virtual memory.
     * @param start the address of the start of the area to be mapped
     * @param size the size, in bytes, of the area to be mapped
     * @return 0 if successful, otherwise the system errno
     */
    public static int mmap(VM_Address start, int size) {
      VM_Address p = Native.mmap(start, 
				 size, 
				 (NativeConstants.PROT_READ | 
				  NativeConstants.PROT_WRITE |
				  NativeConstants.PROT_EXEC), 
				 (NativeConstants.MAP_ANON |
				  NativeConstants.MAP_PRIVATE |
				  NativeConstants.MAP_FIXED)
				 , -1, 0);
      if(p.asInt() == -1) return -1;
      return 0;
    }

    /**
     * Protects access to an area of virtual memory.
     * @param start the address of the start of the area to be mapped
     * @param size the size, in bytes, of the area to be mapped
     * @return <code>true</code> if successful, otherwise <code>false</code>
     */
    public static boolean mprotect(VM_Address start, int size) {
      return Native.mprotect(start, size, NativeConstants.PROT_NONE) == 0;
    }

    /**
     * Allows access to an area of virtual memory.
     * @param start the address of the start of the area to be mapped
     * @param size the size, in bytes, of the area to be mapped
     * @return <code>true</code> if successful, otherwise <code>false</code>
     */
    public static boolean munprotect(VM_Address start, int size) {
      return Native.mprotect(start, size, (NativeConstants.PROT_READ  |
					   NativeConstants.PROT_WRITE |
					   NativeConstants.PROT_EXEC)) == 0;
    }

    /**
     * Zero a region of memory.
     * @param start Start of address range (inclusive)
     * @param len Length in bytes of range to zero Returned: nothing
     */
    public static void zero(VM_Address start, VM_Word len) {
        VM_Address end = start.add(len);
        VM_Word word = VM_Word.fromInt(0);
        while (start.uLT(end)) {
            start.setWord(word);
            start = start.add(VM_Word.widthInBytes());
        }
    }

    /**
     * Zero a range of pages of memory.
     * @param start Start of address range (must be a page address)
     * @param len Length in bytes of range (must be multiple of page size)
     */
    public static void zeroPages(VM_Address start, int len) {
        throw new UnimplementedException("zero pages");
    }

    /**
     * Logs the contents of an address and the surrounding memory to the error
     * output.
     * @param start the address of the memory to be dumped
     * @param beforeBytes the number of bytes before the address to be included
     * @param afterBytes the number of bytes after the address to be included
     */
    public static void dumpMemory(
        VM_Address start,
        int beforeBytes,
        int afterBytes) {
        throw new UnimplementedException("dumpMemory");
    }

    /***************************************************************************
     * Access to object model
     */

    /**
     * Tests a bit available for memory manager use in an object.
     * @param o the address of the object
     * @param idx the index of the bit
     */
    public static boolean testAvailableBit(VM_Address o, int idx) {
        return ((JMTkOop) o.asAnyOop()).testAvailableBit(idx);
    }

    /**
     * Sets a bit available for memory manager use in an object.
     * @param o the address of the object
     * @param idx the index of the bit
     * @param flag <code>true</code> to set the bit to 1,<code>false</code>
     *                to set it to 0
     */
    public static void setAvailableBit(VM_Address o, int idx, boolean flag) {
        ((JMTkOop) o.asAnyOop()).setAvailableBit(idx, flag);
    }

    /**
     * Attempts to set the bits available for memory manager use in an object.
     * The attempt will only be successful if the current value of the bits
     * matches <code>oldVal</code>. The comparison with the current value
     * and setting are atomic with respect to other allocators.
     * @param o the address of the object
     * @param oldVal the required current value of the bits
     * @param newVal the desired new value of the bits
     * @return <code>true</code> if the bits were set, <code>false</code>
     *         otherwise
     */
    public static boolean attemptAvailableBits(
        VM_Address o,
        int oldVal,
        int newVal) {
        return ((JMTkOop) o.asAnyOop()).attemptAvailableBits(oldVal, newVal);
    }

    /**
     * Gets the value of bits available for memory manager use in an object, in
     * preparation for setting those bits.
     * @param o the address of the object
     * @return the value of the bits
     */
    public static int prepareAvailableBits(VM_Address o) {
        return ((JMTkOop) o.asAnyOop()).prepareAvailableBits();
    }

    /**
     * Sets the bits available for memory manager use in an object.
     * @param o the address of the object
     * @param val the new value of the bits
     */
    public static void writeAvailableBitsWord(VM_Address o, int val) {
        ((JMTkOop) o.asAnyOop()).writeAvailableBitsWord(val);
    }

    /**
     * Read the bits available for memory manager use in an object.
     * @param o the address of the object
     * @return the value of the bits
     */
    public static int readAvailableBitsWord(VM_Address o) {
        return ((JMTkOop) o.asAnyOop()).readAvailableBitsWord();
    }

    /**
     * Gets the offset of the memory management header from the object
     * reference address. XXX The object model / memory manager interface
     * should be improved so that the memory manager does not need to know
     * this.
     * @return the offset, relative the object reference address
     */
    /* AJG: Should this be a variable rather than method? */
    public static int GC_HEADER_OFFSET() {
        throw new UnimplementedException("GC_HEADER_OFFSET");
    }

    /**
     * Returns the lowest address of the storage associated with an object.
     * @param object the reference address of the object
     * @return the lowest address of the object
     */
    public static VM_Address objectStartRef(VM_Address object)
        throws VM_PragmaInline {
        return ((JMTkOop) object.asAnyOop()).objectStartRef();
    }

    /**
     * Returns an address guaranteed to be inside the storage assocatied with
     * and object.
     * @param obj the reference address of the object
     * @return an address inside the object
     */
    public static VM_Address refToAddress(VM_Address obj) {
        return objectStartRef(obj);
    }

    /**
     * Checks if a reference of the given type in another object is inherently
     * acyclic. The type is given as a TIB.
     * @return <code>true</code> if a reference of the type is inherently
     *         acyclic
     */
    public static boolean isAcyclic(Object[] tib) {
        return false; // this needs revisited
    }
    
    // triggerCollection's caller.  We must remember to start scanning
    // the current thread's stack there.
    private static Activation triggerActivation;
    /***************************************************************************
     * Trigger collections
     */

    /**
     * Triggers a collection.
     * @param why the reason why a collection was triggered. 0 to <code>TRIGGER_REASONS - 1</code>.
     */
    public static final void triggerCollection(int why)
        throws VM_PragmaInterruptible, PragmaNoInline
    {
	MemoryManager.GCNative.SAVE_REGISTERS();

        triggerActivation = Context.getCurrentContext().getCurrentActivation();
        triggerActivation = triggerActivation.caller(triggerActivation);

        Plan.collectionInitiated();

        if (why == EXTERNAL_GC_TRIGGER) {
            Plan.userTriggeredGC();
        }
        VM_Interface.getPlan().collect();

        if (Plan.isLastGCFull())
            // TODO: && sizeBeforeGC == HeapGrowthManager.getCurrentHeapSize())
            checkForExhaustion(why, false);

        Plan.checkForAsyncCollection();
    }

    /**
     * Trigger an asynchronous collection, checking for memory exhaustion
     * first.
     */
    public static final void triggerAsyncCollection()
        throws VM_PragmaUninterruptible {
        throw new UnimplementedException("triggerAsyncCollection");
        /*
	 * checkForExhaustion(RESOURCE_GC_TRIGGER, true);
	 * Plan.collectionInitiated(); if (Options.verbose >= 1)
	 * VM.sysWrite("[Async GC]");
	 * VM_CollectorThread.asyncCollect(VM_CollectorThread.handshake);
	 */
    }

    public static final void dumpStack() {
        throw new UnimplementedException("dumpStack");
    }

    /**
     * Determine whether a collection cycle has fully completed (this is used
     * to ensure a GC is not in the process of completing, to avoid, for
     * example, an async GC being triggered on the switch from GC to mutator
     * thread before all GC threads have switched.
     * @return True if GC is not in progress.
     */
    public static final boolean noThreadsInGC()
        throws VM_PragmaUninterruptible {
        throw new UnimplementedException("noThreadsInGC");
    }

    /**
     * Check for memory exhaustion, possibly throwing an out of memory
     * exception and/or triggering another GC.
     * @param why Why the collection was triggered
     * @param async True if this collection was asynchronously triggered.
     */
    private static final void checkForExhaustion(int why, boolean async)
        throws VM_PragmaLogicallyUninterruptible {
      
      long reserved = Plan.reservedMemory();
      long total = Plan.totalMemory();
      double usage = Plan.reservedMemory() / ((double) Plan.totalMemory());
      
      //Native.print_string("Checking for exhaustion. Usage: " + usage);
      //Native.print_string("\n");      
      if (usage > OUT_OF_MEMORY_THRESHOLD) {
	if (why == INTERNAL_GC_TRIGGER) {
	  Native.print_string("Out of memory. Trying to throw exception\n");
	  // We really want to call MM_Inteface.outOfMemory(), but we
	  // can't do that outside of the MM_Interface singleton.
	  failWithOutOfMemoryError();
	}
	/* clear all possible reference objects */
	if (!async)
	  triggerCollection(INTERNAL_GC_TRIGGER);
      }
      
      int fromspacesize = Plan.HIGH_SS_START.asInt() - Plan.LOW_SS_START.asInt();
    }
  
    /***************************************************************************
     * Finalizers
     */

    /**
     * Schedule the finalizerThread, if there are objects to be finalized and
     * the finalizerThread is on its queue (ie. currently idle). Should be
     * called at the end of GC after moveToFinalizable has been called, and
     * before mutators are allowed to run.
     */
    public static void scheduleFinalizerThread()
        throws VM_PragmaUninterruptible {
        throw new UnimplementedException("scheduleFinalizerThread");
    }

    /***************************************************************************
     * Collection
     */

    /**
     * Checks if a plan instance is eligible to participate in a collection.
     * @param plan the plan to check
     * @return <code>true</code> if the plan is not participating, <code>false</code>
     *         otherwise
     */
    public static boolean isNonParticipating(Plan plan) {
        return false; // all threads participate
    }

    /**
     * Prepare a plan that is not participating in a collection.
     * @param p the plan to prepare
     */
    public static void prepareNonParticipating(Plan p) {
        /*
	 * Jikes: The collector threads of processors currently running threads
	 * off in JNI-land cannot run. OVM - We don't have threads running
	 * native code
	 */
        /*
	 * VM_Processor vp = (VM_Processor) p; int vpStatus = vp.vpStatus; if
	 * (VM.VerifyAssertions) VM._assert(vpStatus ==
	 * VM_Processor.BLOCKED_IN_NATIVE); // processor & its running thread
	 * are blocked in C for this GC. // Its stack needs to be scanned,
	 * starting from the "top" java frame, which has // been saved in the
	 * running threads JNIEnv. Put the saved frame pointer // into the
	 * threads saved context regs, which is where the stack scan starts. //
	 * VM_Thread t = vp.activeThread;
	 * t.contextRegisters.setInnermost(VM_Address.zero(),
	 * t.jniEnv.topJavaFP());
	 */
    }

    /**
     * Set a collector thread's so that a scan of its stack will start at
     * VM_CollectorThread.run
     * @param p the plan to prepare
     */
    public static void prepareParticipating(Plan p) {
    }

    /***************************************************************************
     * Tracing
     */

    /**
     * Prepares for using the <code>computeAllRoots</code> method. The thread
     * counter allows multiple GC threads to co-operatively iterate through the
     * thread data structure (if load balancing parallel GC threads were not
     * important, the thread counter could simply be replaced by a for loop).
     */
    public static void resetThreadCounter() {
        // Needed for load-balancing GC. Not needed in the OVM
    }

    /**
     * Pre-copy all potentially movable instances used in the course of GC.
     * This includes the thread objects representing the GC threads themselves.
     * It is crucial that these instances are forwarded <i>prior</i> to the
     * GC proper. Since these instances <i>are not</i> enqueued for scanning,
     * it is important that when roots are computed the same instances are
     * explicitly scanned and included in the set of roots. The existence of
     * this method allows the actions of calculating roots and forwarding GC
     * instances to be decoupled. The <code>threadCounter</code> must be
     * reset so that load balancing parallel GC can share the work of scanning
     * threads.
     */
    public static void preCopyGCInstances() {
    }

    /**
     * Computes all roots. This method establishes all roots for collection and
     * places them in the root values, root locations and interior root
     * locations queues. This method should not have side effects (such as
     * copying or forwarding of objects). There are a number of important
     * preconditions:
     * <ul>
     * <li>All objects used in the course of GC (such as the GC thread
     * objects) need to be "pre-copied" prior to calling this method.
     * <li>The <code>threadCounter</code> must be reset so that load
     * balancing parallel GC can share the work of scanning threads.
     * </ul>
     * @param rootLocations set to store addresses containing roots
     * @param interiorRootLocations set to store addresses containing return
     *                adddresses, or <code>null</code> if not required
     */
    public static void computeAllRoots(
        AddressDeque rootLocations,
        AddressPairDeque interiorRootLocations) {

        VM_Address nextref;
       
	int count = 0;

        LocalReferenceIterator lrt =
            LocalReferenceIterator.factory().make(triggerActivation);
        while (lrt.hasNext()) {
            nextref = lrt.next();
            if (nextref.getAddress().isNonNull()) {
	      rootLocations.insert(nextref);
		count++;
            }
        }
	
	//ovm.core.execution.Native.print_string("LocalReferenceIterator found ");
	//ovm.core.execution.Native.print_int(count);
	//ovm.core.execution.Native.print_string("\n");
	count = 0;
        SpaceScanner scanner =
            new SpaceScanner(
                Native.getImageBaseAddress(),
                Native.getImageEndAddress());

	//Native.print_string("Space scanner created. Start address: ");
	//Native.print_int(Native.getImageBaseAddress().asInt());
	//Native.print_string(". End address: ");
	//Native.print_int(Native.getImageEndAddress().asInt());
	//Native.print_string("\n");

        S3Blueprint currbp;
        VM_Address currobj;
	int total = 0;
	int objmod = 0;
	int arr = 0;
	int obj = 0;
        while (scanner.hasNext()) {
	  total++;
	  currobj = scanner.next();
	  
	  for (int k = ObjectModel.getObjectModel().maxReferences();k-- > 0;) {
	    MovingGC ref = (MovingGC) currobj.asOop().getReference(k).asAnyOop();
	    if (null == ref) continue;
	    if (inGCSpace(VM_Address.fromObject(currobj.asOop().getReference(k)))) {
	      RefWrapper r = new RefWrapper(currobj);
	      currbp = (S3Blueprint) VM_Address.fromObject(r).asOop().getBlueprint();
	      int[] refmap = currbp.getRefMap();
	      rootLocations.insert(VM_Address.fromObject(r).add(refmap[0]));
	      //rootLocations.insert(VM_Address.fromObject(currobj).add
	      //		   (((s3.core.domain.S3Model_B_M_J_H)(ObjectModel.getObjectModel())).
	      //		    MONITOR_OFFSET));
	      count++;
	      objmod++;
	    }
	  }
	  
	  currbp = (S3Blueprint) currobj.asOop().getBlueprint();
	  
	  if (currbp.isArray()) {
	    if (((S3Blueprint) currbp.asArray().getComponentBlueprint()).isReference()) {
	      for (int i = 0; i < currbp.asArray().getLength(currobj.asOop()); i++) {
		VM_Address component = (currobj.add(((S3Blueprint.Array) currbp.asArray()).byteOffset(i))).getAddress();
		if (!component.isNull()) {
		  if (inGCSpace(component)) {
		    rootLocations.insert(currobj.add(((S3Blueprint.Array) currbp.asArray()).byteOffset(i)));
		    arr++;
		    count++;
		  }
		}
	      }
	    }
	  } else {
	    int[] refmap = currbp.getRefMap();
	    for (int i = 0; i < refmap.length; i++) {
	      VM_Address fldaddress = currobj.add(refmap[i]);
	      VM_Address fldcontent = fldaddress.getAddress();
	      if (!(fldcontent.isNull()) && inGCSpace(fldcontent)) {
		rootLocations.insert(fldaddress);
		count++;
		obj++;
	      }
	    }
	  }
        }
	//ovm.core.execution.Native.print_string("Object Model: ");
	//ovm.core.execution.Native.print_int(objmod);
	//ovm.core.execution.Native.print_string(" Arrays: ");
	//ovm.core.execution.Native.print_int(arr);
	//ovm.core.execution.Native.print_string(" Objects: ");
	//ovm.core.execution.Native.print_int(obj);
	//ovm.core.execution.Native.print_string("\nSpace scanner found ");
	//ovm.core.execution.Native.print_int(count);
	//ovm.core.execution.Native.print_string(" Scaned: ");
	//ovm.core.execution.Native.print_int(total);	
	//ovm.core.execution.Native.print_string("\n");
    }

    private static boolean inGCSpace(VM_Address addr) {
      return (VMResource.getSpace(addr) != getPlan().BOOT_SPACE &&
	      VMResource.getSpace(addr) != getPlan().UNUSED_SPACE);
    }

    /***************************************************************************
     * Copying
     */

    /**
     * Copy an object using a plan's allocCopy to get space and install the
     * forwarding pointer. On entry, <code>fromObj</code> must have been
     * reserved for copying by the caller. This method calls the plan's <code>PostCopy</code>
     * method after making the copy.
     * @param fromObj the address of the object to be copied
     * @param forwardingPtr the value the forwarding pointer in the copy is to
     *                be set to. This value is first modified by the plan's
     *                <code>resetGCBitsForCopy</code> method. AJG: Not sure
     *                why this value passed in, it seems that it could be
     *                simply copied from the old object.
     * @return the address of the new object
     */
    public static VM_Address copy(VM_Address fromObj, int forwardingPtr)
        throws VM_PragmaInline {
        Oop o =  fromObj.asOop();
        int numBytes = o.getBlueprint().getVariableSize(o);

        forwardingPtr =
            Plan.resetGCBitsForCopy(fromObj, forwardingPtr, numBytes);
        VM_Address to =
            plan.allocCopy(
                fromObj.getAddress(),
                numBytes,
                !(o.getBlueprint().isArray()));
	Mem.the().cpy(to, fromObj, numBytes);

        plan.postCopy(to, null, numBytes, !(o.getBlueprint().isArray()));

	attemptAvailableBits(to,
			     prepareAvailableBits(to),
			     forwardingPtr);

        return to;
    }

    /***************************************************************************
     * References
     */

    /**
     * Determine whether this reference has ever been enqueued.
     * @param r the Reference object
     * @return <code>true</code> if reference has ever been enqueued
     */
    public static final boolean referenceWasEverEnqueued(Reference r) {
        throw new UnimplementedException("referenceWasEverEnqueued");
    }

    /**
     * Put this Reference object on its ReferenceQueue (if it has one) when its
     * referent is no longer sufficiently reachable. The definition of
     * "reachable" is defined by the semantics of the particular subclass of
     * Reference. The implementation of this routine is determined by the the
     * implementation of java.lang.ref.ReferenceQueue in GNU classpath. It is
     * in this class rather than the public Reference class to ensure that
     * Jikes has a safe way of enqueueing the object, one that cannot be
     * overridden by the application program.
     * @see java.lang.ref.ReferenceQueue
     * @param r the Reference object
     * @return <code>true</code> if the reference was enqueued
     */
    public static final boolean enqueueReference(Reference r) {
        throw new UnimplementedException("enqueueReference");
    }

    /***************************************************************************
     * Miscellaneous
     */

    /**
     * Sets the range of addresses associated with a heap.
     * @param id the heap identifier
     * @param start the address of the start of the heap
     * @param end the address of the end of the heap
     */
    public static void setHeapRange(int id, VM_Address start, VM_Address end)
        throws VM_PragmaUninterruptible {
        // Used for sending signals to Java code in jikes
    }

    private static void notifyInit() throws BCdead {
	System.err.println("Plan allocated");
    }

    /**
     * Gets the plan associated with the current processor.
     * @return the plan for the current processor
     */
    public static Plan getPlan() throws VM_PragmaInline {
	if (plan == null) {
	    notifyInit();
	    plan = new Plan();
	}
        return plan;
    }

    /**
     * Read cycle counter
     */
    public static long cycles() {
        return 0;
    }

    /**
     * Convert cycles to milliseconds
     */
    public static double cyclesToMillis(long c) {
        return 0;
    }

    /**
     * Convert cycles to seconds
     */
    public static double cyclesToSecs(long c) {
        return 0;
    }

    /**
     * Convert milliseconds to cycles
     */
    public static long millisToCycles(double t) {
        return 0;
    }

    /**
     * Convert seconds to cycles
     */
    public static long secsToCycles(double t) {
        return 0;
    }

    /*
     * Utilities from the VM class
     */

    /**
     * Checks that the given condition is true. If it is not, this method does
     * a traceback and exits.
     * @param cond the condition to be checked
     */
    public static void _assert(boolean cond) throws VM_PragmaInline {
        OVMBase.OVMassert(cond);
    }

    /**
     * Checks if the virtual machine is running. This value changes, so the
     * call-through to the VM must be a method. In Jikes RVM, just returns
     * VM.runningVM.
     * @return <code>true</code> if the virtual machine is running
     */
    public static boolean runningVM() {
        return MM_Interface.runningVM;
    }

    /***************************************************************************
     * Logging
     */

    /**
     * Logs a message and traceback, then exits.
     * @param message the string to log
     */
    public static void sysFail(String message) {
        OVMBase.OVMassert(message);
    }

    public static void sysExit(int rc) throws VM_PragmaUninterruptible {
        Native.exit_process(rc);
    }

    /**
     * Copies characters from the string into the character array. Thread
     * switching is disabled during this method's execution.
     * <p>
     * <b>TODO:</b> There are special memory management semantics here that
     * someone should document.
     * @param src the source string
     * @param dst the destination array
     * @param dstBegin the start offset in the desination array
     * @param dstEnd the index after the last character in the destination to
     *                copy to
     * @return the number of characters copied.
     */
    public static int copyStringToChars(String src,
					char[] dst,
					int dstBegin,
					int dstEnd)
      throws VM_PragmaLogicallyUninterruptible {
      int len =	src.length(); 
      int n = (dstBegin + len <= dstEnd) ? len : (dstEnd - dstBegin); 
      for (int i = 0; i < n; i++) 
	setArrayNoBarrier(dst, dstBegin + i, src.charAt(i)); 
      return n; 
    }

    /**
     * Sets an element of a char array without invoking any write barrier. This
     * method is called by the Log method, as it will be used during garbage
     * collection and needs to manipulate character arrays without causing a
     * write barrier operation.
     *
     * Umm...  what barriers would these be?  Its a char array!
     *
     * @param dst the destination array
     * @param index the index of the element to set
     * @param value the new value for the element
     */
    public static void setArrayNoBarrier(char[] dst, int index, char value) {
        dst[index] = value;
    }

    /**
     * Gets an element of a char array without invoking any read barrier. This
     * method is called by the Log method, as it will be used during garbage
     * collection and needs to manipulate character arrays without causing a
     * read barrier operation.
     * @param src the source array
     * @param index the index of the element to get
     * @return the new value of element
     */
    public static char getArrayNoBarrier(char[] src, int index) {
        return src[index];
    }

    /**
     * Gets an element of a byte array without invoking any read barrier. This
     * method is called by the Log method, as it will be used during garbage
     * collection and needs to manipulate character arrays without causing a
     * read barrier operation.
     * @param src the source array
     * @param index the index of the element to get
     * @return the new value of element
     */
    public static byte getArrayNoBarrier(byte[] src, int index) {
        return src[index];
    }

    /**
     * Log a message.
     * @param c character array with message starting at index 0
     * @param len number of characters in message
     */
    private static void sysWrite(char[] c, int len) {
throw new Error("unimplemented");
        //     Native.puts(c);
    }

    /**
     * Log a thread identifier and a message.
     * @param c character array with message starting at index 0
     * @param len number of characters in message
     */
    public static void sysWriteThreadId(char[] c, int len) {
        throw new UnimplementedException("sysWriteThreadID");
    }

    /**
     * Get the type descriptor for an object.
     * @param ref address of the object
     * @return byte array with the type descriptor
     */
    public static byte[] getTypeDescriptor(VM_Address ref) {
      return new byte[0];
    }

    /**
     * Get the long for an address
     * @param addr the address
     * @return long for the address
     */
    public static long addressToLong(VM_Address addr) {
      return addr.asInt();
    }

    /* Used in processing weak references etc */

    public static VM_Address getReferent(VM_Address addr) {
        return null;
    }

    public static void setReferent(VM_Address addr, VM_Address referent) {
    }

    public static VM_Address getNextReferenceAsAddress(VM_Address ref) {
        return null;
    }

    public static void setNextReferenceAsAddress(
        VM_Address ref,
        VM_Address next) {
    }

    /**
     * Rendezvous with all other processors, returning the rank (that is, the
     * order this processor arrived at the barrier).
     */
    public static int rendezvous(int where) throws VM_PragmaUninterruptible {
      return 1;
    }

    /**
     * Primitive parsing facilities for strings
     */
    public static int primitiveParseInt(String value)
        throws VM_PragmaInterruptible {
        throw new UnimplementedException("primitiveParseInt");
    }
    public static float primitiveParseFloat(String value)
        throws VM_PragmaInterruptible {
        throw new UnimplementedException("primitiveParseFloat");
    }

    /**
     * Throw an out of memory exception.
     */

    public static void failWithOutOfMemoryError()
        throws VM_PragmaLogicallyUninterruptible, VM_PragmaNoInline
    {
	CoreServicesAccess csa 
	    = DomainDirectory.getExecutiveDomain().getCoreServicesAccess();
	csa.generateThrowable(Throwables.OUT_OF_MEMORY_ERROR, 0);
    }

}

class RefWrapper{
  Object ref;
  public RefWrapper(VM_Address ref){
    this.ref = ref.asObject();
  }
}
