/*
 * (C) Copyright IBM Corp. 2001
 */
//$Id: ScanObject.java,v 1.6 2004/05/28 20:24:58 jv Exp $

package s3.core.services.memory.vmInterface;

import ovm.core.domain.ObjectModel;
import ovm.core.domain.Oop;
import ovm.core.services.memory.VM_Address;
import s3.core.domain.S3Blueprint;
import s3.core.services.memory.JMTk.BasePlan;
import s3.core.services.memory.JMTk.Enumerate;
import s3.core.services.memory.JMTk.VMResource;
import s3.core.services.memory.JMTk.VM_PragmaInline;
import s3.core.services.memory.JMTk.VM_PragmaNoInline;
import s3.core.services.memory.JMTk.VM_PragmaUninterruptible;


/*
 * ; import s3.core.services.memory.VM; import
 * s3.core.services.memory.VM_Constants; import
 * s3.core.services.memory.VM_Magic; import
 * s3.core.services.memory.VM_ObjectModel; import
 * s3.core.services.memory.VM_Scheduler; import
 * s3.core.services.memory.VM_Memory; import s3.core.services.memory.VM_Time;
 * import s3.core.services.memory.VM_Entrypoints; import
 * s3.core.services.memory.VM_Reflection; import
 * s3.core.services.memory.VM_Synchronization;
 */
/**
 * Class that supports scanning Objects or Arrays for references during
 * Collection and processing those references
 * @author Stephen Smith
 */
public class ScanObject {

    /**
     * Scan a object, processing each pointer field encountered. The object is
     * not known to be a root object.
     * @param object The object to be scanned.
     */
    public static void scan(VM_Address object)
        throws VM_PragmaUninterruptible, VM_PragmaInline {
        scan(object, null);
    }

    /**
     * Enumerate the pointers in an object, calling back to a given plan for
     * each pointer encountered. <i>NOTE</i> that only the "real" pointer
     * fields are enumerated, not the TIB.
     * @param object The object to be scanned.
     * @param enum the Enumerate object through which the callback is made
     */
    public static void enumeratePointers(VM_Address object, Enumerate enum)
        throws VM_PragmaUninterruptible, VM_PragmaInline {
        scan(object, enum);
    }

    /**
     * Enumerate the pointers in an object, calling back to a given plan for
     * each pointer encountered. <i>NOTE</i> that only the "real" pointer
     * fields are enumerated, not the TIB.
     * @param object The object to be scanned.
     * @param enum the Enumerate object through which the callback is made
     */
    public static void enumeratePointers(Object object, Enumerate enum)
        throws VM_PragmaUninterruptible, VM_PragmaInline {
        scan(VM_Address.fromObject(object), enum);
    }

    /**
     * Scans an object or array for internal object references and processes
     * those references (calls processPtrLocation)
     * @param objRef reference for object to be scanned
     * @param enum the Enumerate object though which the callback is made
     */
    private static void scan(VM_Address objRef, Enumerate enum)
        throws VM_PragmaUninterruptible, VM_PragmaInline {
      Oop oop = objRef.asOop();
      
      if (VM_Interface.VerifyAssertions)
	VM_Interface._assert(!objRef.isNull());
      
      for (int k = ObjectModel.getObjectModel().maxReferences(); k-- > 0;) {
	Oop ref = oop.getReference(k);
	if (ref == null)
	  continue;
	ref = MM_Interface.processOopValue(ref);
	((Oop.WithUpdate)oop.asAnyOop()).updateReference(k, ref);
      }
      
      S3Blueprint type = (S3Blueprint) oop.getBlueprint();
      if(type.isArray()){
	  S3Blueprint elementType =
	    (S3Blueprint) type.asArray().getComponentBlueprint();
	  if (elementType.isReference()) {
	    int num_elements = type.asArray().getLength(objRef.asOop());
	    int numBytes = num_elements * VM_Address.widthInBytes();
	    VM_Address location =
	      objRef.add(((S3Blueprint.Array) type).byteOffset(0));
	    VM_Address end = location.add(numBytes);
	    while (location.uLT(end)) {
	      if (enum != null)
		enum.enumeratePointerLocation(location);
	      else
		MM_Interface.processPtrLocation(location);
	      location = location.add(VM_Address.widthInBytes());
	    }
	    //        Statistics.profileScan(obj, numBytes, type);
	  }
	}
        else {
	  int[] refOffsets = type.getRefMap();
	  for (int i = 0, n = refOffsets.length; i < n; i++) {
	    VM_Address location = objRef.add(refOffsets[i]);
	    VM_Address contents = location.getAddress();
	    //ovm.core.execution.Native.print_string("reference at offset ");
	    //ovm.core.execution.Native.print_int(refOffsets[i]);
	    //ovm.core.execution.Native.print_string(" is ");
	    //ovm.core.execution.Native.print_int(objRef.add(refOffsets[i]).asInt());
	    //ovm.core.execution.Native.print_string(" referring to ");
	    //ovm.core.execution.Native.print_int(contents.asInt());
	    //ovm.core.execution.Native.print_string("\n");
	    if(VMResource.getSpace(contents) != BasePlan.UNUSED_SPACE)
	      if (enum != null)
                    enum.enumeratePointerLocation(location);
	      else
		MM_Interface.processPtrLocation(location);
	  }
	  //Statistics.profileScan(obj, 4 * refOffsets.length, type);
        }
    }

    public static boolean validateRefs(VM_Address ref, int depth)
        throws VM_PragmaUninterruptible, VM_PragmaNoInline {

        if (ref.isNull())
            return true; // null is always valid

        // First check passed ref, before looking into it for refs
        if (!Util.validRef(ref)) {
            //        VM_Interface.sysWrite("ScanObject.validateRefs: Bad Ref = ");
            //        VM_Interface.sysWrite(ref);
            return false;
        }

        if (depth == 0)
            return true; //this ref valid, stop depth first scan
        Oop oop = ref.asOop();
        S3Blueprint type = (S3Blueprint) oop.getBlueprint();
        if (type.isReference()) {
            int[] refOffsets = type.getRefMap();
            for (int i = 0, n = refOffsets.length; i < n; i++) {
                VM_Address location = ref.add(refOffsets[i]);
                VM_Address iref = location.getAddress();
                if (!validateRefs(iref, depth - 1)) {
                    //  VM_Interface.sysWrite("Referenced from object: Ref = ");
                    //  VM_Interface.sysWrite(ref);
                    // VM_Interface.sysWrite(" At offset = ");
                    // VM_Interface.sysWriteln(refOffsets[i]);
                }
            }
        } else {
            S3Blueprint elementType =
                (S3Blueprint) type.asArray().getComponentBlueprint();
            if (elementType.isReference()) {
                int num_elements = type.asArray().getLength(ref.asOop());
                int numBytes = num_elements * VM_Address.widthInBytes();
                VM_Address location =
                    ref.add(((S3Blueprint.Array) type).byteOffset(0));
                VM_Address end = location.add(numBytes);
                while (location.uLT(end)) {
                    VM_Address iref = location.getAddress();
                    if (!validateRefs(iref, depth - 1)) {
                        //  VM_Interface.sysWrite("Referenced from Array: Ref =
                        // ", ref);
                        //  VM_Interface.sysWrite(" At Index = ", location>>2);
                        //  VM_Interface.sysWriteln(" Array Length = ",
                        // num_elements);
                        return false;
                    }
                    location = location.add(VM_Address.widthInBytes());
                }
            }
        }
        return true;
    } // validateRefs

} // ScanObject
