/**
 * simple test for class finalization
 *
 * @author Godmar Back <gback@cs.utah.edu>
 */
import java.io.*;
import java.lang.reflect.*;

public class ClassGC extends ClassLoader {

    /*
     * read a .class file
     */
    static byte [] readin(String name) throws Exception
    {
        File cf = new File(name);
        FileInputStream cfi = new FileInputStream(cf);

        int len = (int)cf.length();
        byte [] cb = new byte[len];
        if (cfi.read(cb) != len)
            throw new Exception("short read for " + name);
        return cb;
    }

    static boolean verbose = false;

    public Class loadClass(String name, boolean resolve) 
	throws ClassNotFoundException 
    {
	Class c = null;
	if (!name.startsWith("ClassGCTest")) {
	    if (verbose)
		System.out.println("finding " + name);
	    return findSystemClass(name);
	} else {
	    if (verbose)
		System.out.println("loading " + name);
	    try {
		byte []b = readin(name + ".class");
		return defineClass(name, b, 0, b.length);
	    } catch (Exception e) {
		System.out.println(e);
		return null;
	    }
	}
    }

    /*
     * load a class and create an instance, but drop the class
     */
    static ClassLoader doit(String testclass) throws Exception {
	ClassGC l = new ClassGC();
	l.loadClass(testclass).newInstance();
	return l;
    }

    static void cleanup() {
	System.gc();
	System.runFinalization();
    }

    public static void main(String av[]) throws Exception {
	verbose = av.length > 0;
	String testclass = "ClassGCTest";

	for (int i = 0; i < 10; i++) {
	    ClassLoader l = doit(testclass);
	    cleanup();
	    /* this class refers to the former to which we didn't keep
	     * a ref.  If our class gc is too eager, it will have freed it
	     * and bad things will happen.
	     */
	    Class c = l.loadClass("ClassGCTestLater");
	    c.newInstance();
	}
    }
    public static boolean gotOne;
}

class ClassGCTest
{
	public static class HObject {
		protected void finalize() throws Throwable {
			if (!ClassGC.gotOne) {
				ClassGC.gotOne = true;
				System.out.println("Success.");
			}
		}
	}

	public static Object f = new HObject();
}

class ClassGCTestLater
{
    public ClassGCTestLater() throws Exception
    {
	Class c = ClassGCTest.class;
	String s = c.getName();
	if (!s.equals("ClassGCTest"))
	    System.out.println("Failure: name is " + s);
	/* I think getConstructor should be enough, since we're in the
           same package, but it fails :-(  -oliva */
	Constructor cc = c.getDeclaredConstructor(new Class [] {});
	if (!cc.toString().equals("ClassGCTest()"))
	    System.out.println("Failure: name is " + cc.toString());
	if (!cc.newInstance(new Object[] {}).
		toString().startsWith("ClassGCTest")) {
	    System.out.println("Failure newInstance.");
	}
    }
}


/* Expected Output:
Success.
*/

@@ -1891,18 +1891,60 @@
 n gotOne
+ForF;%0A    public static boolean gotOneForG
 ;%0A
-
 %7D%0A%0Aclass
@@ -2058,16 +2058,20 @@
 C.gotOne
+ForF
 ) %7B%0A%09%09%09%09
@@ -2084,16 +2084,20 @@
 C.gotOne
+ForF
  = true;
@@ -2186,16 +2186,400 @@
 bject();
+%0A%0A%09/* Make sure interfaces are GC'd also */%0A%09public interface HInterface %7B%0A%09%09void func();%0A%09%7D%0A%0A%09public static class HImplementor implements HInterface %7B%0A%09%09public void func()%0A%09%09%7B%0A%09%09%7D%0A%0A%09%09protected void finalize() throws Throwable %7B%0A%09%09%09if (!ClassGC.gotOneForG) %7B%0A%09%09%09%09ClassGC.gotOneForG = true;%0A%09%09%09%09System.out.println(%22Success.%22);%0A%09%09%09%7D%0A%09%09%7D%0A%09%7D%0A%0A%09public static Object g = new HImplementor();
 %0A%7D%0A%0Aclas
@@ -3216,16 +3216,16 @@
   %7D%0A%7D%0A%0A%0A
-
 /* Expec
@@ -3245,11 +3245,20 @@
 uccess.%0A
+Success.%0A
 */%0A

Check for proper interface GC'ing.