changeset 5383:b3b65a3d441e lambda-b48

Summary: Added shapegen tests for both javac and separate compilation
author kamg
date Thu, 12 Jul 2012 13:07:55 -0400
parents 5d9f63007e58
children ce7fe6306841 b5040ffc66fc
files test-ng/build.xml test-ng/tests/org/openjdk/tests/javac/FDTest.java test-ng/tests/org/openjdk/tests/separate/Compiler.java test-ng/tests/org/openjdk/tests/separate/SourceModel.java test-ng/tests/org/openjdk/tests/separate/TestHarness.java test-ng/tests/org/openjdk/tests/shapegen/ClassCase.java test-ng/tests/org/openjdk/tests/shapegen/Hierarchy.java test-ng/tests/org/openjdk/tests/shapegen/HierarchyGenerator.java test-ng/tests/org/openjdk/tests/shapegen/Rule.java test-ng/tests/org/openjdk/tests/shapegen/RuleGroup.java test-ng/tests/org/openjdk/tests/shapegen/TTNode.java test-ng/tests/org/openjdk/tests/shapegen/TTParser.java test-ng/tests/org/openjdk/tests/shapegen/TTShape.java test-ng/tests/org/openjdk/tests/vm/FDSeparateCompilationTest.java
diffstat 14 files changed, 1783 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/test-ng/build.xml	Sat Jun 30 15:09:37 2012 -0700
+++ b/test-ng/build.xml	Thu Jul 12 13:07:55 2012 -0400
@@ -41,12 +41,13 @@
             <jvmarg value="-ea" />
             <jvmarg value="-esa" />
             <jvmarg value="-Xverify:all" />
+            <jvmarg value="-Xmx2g" />
             <sysproperty key="lambda.metafactory" value="${lambda.metafactory}" />
         </testng>
     </target>
 
     <target name="clean">
-        <delete includeEmptyDirs="true" failonerror="true">
+        <delete includeEmptyDirs="true" failonerror="false">
             <fileset dir="${build.dir}" />
             <fileset dir="${generated.dir}" />
         </delete>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-ng/tests/org/openjdk/tests/javac/FDTest.java	Thu Jul 12 13:07:55 2012 -0400
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.openjdk.tests.javac;
+
+import org.openjdk.tests.shapegen.*;
+
+import com.sun.source.util.JavacTask;
+import com.sun.tools.javac.util.Pair;
+
+import java.net.URI;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.tools.Diagnostic;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.ToolProvider;
+
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeSuite;
+import org.testng.annotations.DataProvider;
+import static org.testng.Assert.*;
+
+public class FDTest {
+    
+    public enum TestKind {
+        POSITIVE,
+        NEGATIVE;
+        
+        Collection<Hierarchy> getHierarchy(HierarchyGenerator hg) {
+            return this == POSITIVE ?
+                    hg.getOK() : hg.getErr();
+        }
+    }
+
+    public static JavaCompiler comp;
+    public static StandardJavaFileManager fm;
+
+    @BeforeSuite 
+    static void init() {
+        // create default shared JavaCompiler - reused across multiple 
+        // compilations
+
+        comp = ToolProvider.getSystemJavaCompiler();
+        fm = comp.getStandardFileManager(null, null, null);
+    }
+
+    public static void main(String[] args) throws Exception {
+        init();
+
+        for (Pair<TestKind,Hierarchy> fdtest : generateCases()) {
+            runTest(fdtest.fst, fdtest.snd, comp, fm);
+        }
+    }
+
+    @Test(dataProvider = "fdCases")
+    public void testOneCase(TestKind tk, Hierarchy hs) 
+            throws Exception {
+        FDTest.runTest(tk, hs, comp, fm);
+    }
+
+    @DataProvider(name = "fdCases")
+    public Object[][] caseGenerator() {
+        List<Pair<TestKind, Hierarchy>> cases = generateCases();
+        Object[][] fdCases = new Object[cases.size()][];
+        for (int i = 0; i < cases.size(); ++i) {
+            fdCases[i] = new Object[2];
+            fdCases[i][0] = cases.get(i).fst;
+            fdCases[i][1] = cases.get(i).snd;
+        }
+        return fdCases;
+    }
+
+    public static List<Pair<TestKind, Hierarchy>> generateCases() {
+        ArrayList<Pair<TestKind,Hierarchy>> list = new ArrayList<>();
+        HierarchyGenerator hg = new HierarchyGenerator();
+        for (TestKind tk : TestKind.values()) {
+            for (Hierarchy hs : tk.getHierarchy(hg)) {
+                list.add(new Pair<>(tk, hs));
+            }
+        }
+        return list;
+    }
+
+    public static void runTest(TestKind tk, Hierarchy hs, 
+            JavaCompiler comp, StandardJavaFileManager fm) throws Exception {
+        new FDTest(tk, hs).run(comp, fm);
+    }
+    
+    TestKind tk;
+    Hierarchy hs;
+    DefenderTestSource source;
+    DiagnosticChecker diagChecker;
+
+    public FDTest() {}
+
+    FDTest(TestKind tk, Hierarchy hs) {
+        this.tk = tk;
+        this.hs = hs;
+        this.source = new DefenderTestSource();
+        this.diagChecker = new DiagnosticChecker();
+    }
+    
+    void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception {
+        JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker,
+                null, null, Arrays.asList(source));
+        try {
+            ct.analyze();
+        } catch (Throwable ex) {
+            fail("Error thrown when analyzing the following source:\n" + source.getCharContent(true));
+        }
+        check();
+    }
+
+    void check() {        
+        boolean errorExpected = tk == TestKind.NEGATIVE;
+        if (errorExpected != diagChecker.errorFound) {
+            fail("problem in source: \n" +
+                 "\nerror found = " + diagChecker.errorFound +
+                 "\nerror expected = " + errorExpected +
+                 "\n" + dumpHierarchy() + 
+                 "\n" + source.getCharContent(true));
+        }
+    }
+    
+    String dumpHierarchy() {
+        StringBuilder buf = new StringBuilder();
+        buf.append("root = " + hs.root + "\n");
+        for (ClassCase cc : hs.all) {
+            buf.append("  class name = " + cc.getName() + "\n");
+            buf.append("    class OK = " + cc.get_OK() + "\n");
+            buf.append("    prov = " + cc.get_mprov() + "\n");
+            
+        }
+        return buf.toString();
+    }
+    
+    class DefenderTestSource extends SimpleJavaFileObject {
+
+        String source;
+
+        public DefenderTestSource() {
+            super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
+            StringBuilder buf = new StringBuilder();
+            List<ClassCase> defaultRef = new ArrayList<>();
+            for (ClassCase cc : hs.all) {
+                Hierarchy.genClassDef(buf, cc, null, defaultRef);
+            }
+            source = buf.toString();
+        }
+
+        @Override
+        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+            return source;
+        }
+    }
+    
+    static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
+
+        boolean errorFound;
+
+        public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+            if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
+                errorFound = true;
+            }
+        }
+    }
+}
--- a/test-ng/tests/org/openjdk/tests/separate/Compiler.java	Sat Jun 30 15:09:37 2012 -0700
+++ b/test-ng/tests/org/openjdk/tests/separate/Compiler.java	Thu Jul 12 13:07:55 2012 -0400
@@ -46,7 +46,10 @@
             System.getProperty("user.dir") + File.separator + "gen-separate");
     private static final File root = new File(targetDir);
 
+    private List<File> tempDirs;
     private boolean verbose;
+    private JavaCompiler systemJavaCompiler;
+    private StandardJavaFileManager fm;
 
     private static class SourceFile extends SimpleJavaFileObject {
         private final String content;
@@ -65,8 +68,13 @@
 
     public Compiler(boolean verbose) { 
         this.verbose = verbose;
+        this.tempDirs = new ArrayList<>();
+        this.systemJavaCompiler = ToolProvider.getSystemJavaCompiler();
+        this.fm = systemJavaCompiler.getStandardFileManager(null, null, null);
     }
 
+    public void setVerbose(boolean v) { this.verbose = v; }
+
     /**
      * Compile hierarchies starting with each of the 'types' and return 
      * a ClassLoader that can be used to load the compiled classes.
@@ -123,11 +131,8 @@
 
         type.generate(accum);
 
-        JavaCompiler systemJavaCompiler = ToolProvider.getSystemJavaCompiler();
-        StandardJavaFileManager fm = 
-            systemJavaCompiler.getStandardFileManager(null, null, null);
-        JavacTask ct = (JavacTask)systemJavaCompiler.getTask(
-            null, fm, null, null, null, files);
+        JavacTask ct = (JavacTask)this.systemJavaCompiler.getTask(
+            null, this.fm, null, null, null, files);
         File destDir = null;
         do {
             int value = counter.incrementAndGet();
@@ -144,8 +149,8 @@
 
         try { 
             destDir.mkdirs();
-            fm.setLocation(StandardLocation.CLASS_OUTPUT, 
-                           Arrays.asList(destDir));
+            this.fm.setLocation(
+                StandardLocation.CLASS_OUTPUT, Arrays.asList(destDir));
         } catch (IOException e) {
             throw new RuntimeException(
                 "IOException encountered during compilation");
@@ -155,6 +160,15 @@
             throw new RuntimeException(
                 "Compilation failure in " + type.getName() + " unit");
         }
+        this.tempDirs.add(destDir);
         return destDir;
     }
+
+    void cleanup() {
+        tempDirs.forEach(dir -> { 
+            Arrays.asList(dir.listFiles()).forEach(file -> { file.delete(); });
+            dir.delete(); 
+        });
+        tempDirs = new ArrayList<>();
+    }
 }
--- a/test-ng/tests/org/openjdk/tests/separate/SourceModel.java	Sat Jun 30 15:09:37 2012 -0700
+++ b/test-ng/tests/org/openjdk/tests/separate/SourceModel.java	Thu Jul 12 13:07:55 2012 -0400
@@ -120,6 +120,18 @@
         public Class getSuperclass() { return null; }
         protected abstract void setSuperClass(Extends supertype);
 
+        public void addSuperType(Extends sup) {
+            assert sup.getType() instanceof Interface : "Must be an interface";
+            this.supertypes.add(sup);
+        }
+        public void addSuperType(Interface iface) {
+            this.supertypes.add(new Extends(iface));
+        }
+
+        public void addMethod(Method m) {
+            this.methods.add(m);
+        }
+
         // Convenience method for creation.  Parameters are interpreted
         // according to their type.  Class (or Extends with a Class type) is
         // considered a superclass (only one allowed).  TypeParameters are
@@ -133,16 +145,16 @@
                 if (ext.supertype instanceof Class) {
                     setSuperClass(ext);
                 } else if (ext.supertype instanceof Interface) {
-                    this.supertypes.add(ext);
+                    addSuperType(ext);
                 } else {
                     assert false : "What is this thing?";
                 }
             } else if (p instanceof Interface) {
-                this.supertypes.add(new Extends((Interface)p));
+                addSuperType((Interface)p);
             } else if (p instanceof TypeParameter) {
                 this.parameters.add((TypeParameter)p);
             } else if (p instanceof Method) {
-                this.methods.add((Method)p);
+                addMethod((Method)p);
             } else {
                 assert false : "What is this thing?";
             }
@@ -228,16 +240,19 @@
     
     public static class Class extends Type {
         private Extends superClass; 
+        private boolean isAbstract;
     
         public Class(String name, List<TypeParameter> params, 
             Extends sprClass, List<Extends> interfaces, List<Method> methods) {
             super(name, params, interfaces, methods);
             this.superClass = sprClass;
+            this.isAbstract = false;
         }
 
         public Class(String name, Element ... components) {
             super(name, null, null, null);
             this.superClass = null;
+            this.isAbstract = false;
 
             for (Element p : components) {
                 addComponent(p);
@@ -245,16 +260,24 @@
         }
 
         @Override
-        protected void setSuperClass(Extends ext) {
+        public void setSuperClass(Extends ext) {
             assert this.superClass == null : "Multiple superclasses defined";
+            assert ext.getType() instanceof Class : "Must be a class";
             this.superClass = ext;
         }
 
+        public void setSuperClass(Class c) {
+            setSuperClass(new Extends(c));
+        }
+
         @Override
         public Class getSuperclass() { 
             return superClass == null ? null : (Class)superClass.supertype; 
         }
 
+        public boolean getIsAbstract() { return this.isAbstract; }
+        public void setIsAbstract(boolean v) { this.isAbstract = v; }
+
         public void generate(SourceProcessor processor) {
             StringWriter sw = new StringWriter();
             PrintWriter pw = new PrintWriter(sw);
@@ -263,7 +286,7 @@
         }
 
         public void generate(PrintWriter pw) {
-            pw.print("public class ");
+            pw.printf("public %sclass ", getIsAbstract() ? "abstract " : "");
             generateName(pw);
             if (superClass != null) {
                 pw.print("extends ");
--- a/test-ng/tests/org/openjdk/tests/separate/TestHarness.java	Sat Jun 30 15:09:37 2012 -0700
+++ b/test-ng/tests/org/openjdk/tests/separate/TestHarness.java	Thu Jul 12 13:07:55 2012 -0400
@@ -41,9 +41,23 @@
 
 public class TestHarness {
 
+    /**
+     * Creates a per-thread persistent compiler object to allow as much 
+     * sharing as possible, but still allows for parallel execution of tests.
+     */
+    protected ThreadLocal<Compiler> compilerLocal = new ThreadLocal<Compiler>() {
+         protected synchronized Compiler initialValue() {
+             return new Compiler(false);
+         }
+     };
+
     protected boolean verbose;
     public static final String stdMethodName = SourceModel.stdMethodName;
 
+    protected TestHarness() {
+        this.verbose = false;
+    }
+
     public void debugTest() {
         this.verbose = true;
     }
@@ -60,9 +74,13 @@
             clsName = clsName.substring(clsName.lastIndexOf(".") + 1);
             System.out.println("Test " + clsName + "." + 
                                result.getName() + " FAILED");
-        }
+        } 
     }
 
+    private static final ConcreteMethod stdCM = ConcreteMethod.returns("-1");
+    private static final PresentMethod stdPM = 
+            new PresentMethod("int", stdMethodName);
+
     /**
      * Returns a class which has a static method with the same name as 
      * 'method', whose body creates an new instance of 'specimen' and invokes
@@ -84,8 +102,13 @@
         String params = 
             Arrays.asList(args).into(new StringJoiner(", ")).toString();
 
-        Class iv = new Class("IV", StaticMethod.returns(String.format(
-            "(new %s()).%s(%s)", specimen.getName(), method.getName(), params)));
+        StaticMethod sm = new StaticMethod(
+            method.getReturnType(), method.getName(), 
+            String.format("return (new %s()).%s(%s);", 
+                          specimen.getName(), method.getName(), params));
+
+        Class iv = new Class("IV", sm);
+
         iv.addCompilationDependency(stub);
         iv.addCompilationDependency(cm);
 
@@ -125,6 +148,9 @@
      * Uses 'loader' to load class 'clzz', and calls the static method
      * 'method'.  If the return value does not equal 'value' (or if an
      * exception is thrown), then a test failure is indicated.
+     *
+     * If 'value' is null, then no equality check is performed -- the assertion
+     * fails only if an exception is thrown.
      */
     protected void assertStaticCallEquals(
             ClassLoader loader, Class clzz, String method, Object value) {
@@ -143,7 +169,9 @@
         try {
             Object res = m.invoke(null);
             assertNotNull(res);
-            assertEquals(res, value);
+            if (value != null) { 
+                assertEquals(res, value);
+            }
         } catch (InvocationTargetException | IllegalAccessException e) {
             fail("Unexpected exception thrown: " + e.getCause());
         }
@@ -159,11 +187,14 @@
             Object value, Class target, ConcreteMethod method, 
             String returns, String ... args) {
 
+        Compiler compiler = compilerLocal.get();
+        compiler.setVerbose(this.verbose);
+
         Class iv = invokeVirtualHarness(target, method, returns, args);
-        Compiler compiler = new Compiler(this.verbose);
         ClassLoader loader = compiler.compile(iv, target);
 
         assertStaticCallEquals(loader, iv, method.getName(), value);
+        compiler.cleanup();
     }
 
     /**
@@ -171,8 +202,8 @@
      * a return type of 'int', and no arguments.
      */
     public void assertInvokeVirtualEquals(int value, Class target) {
-        ConcreteMethod method = ConcreteMethod.returns("-1");
-        assertInvokeVirtualEquals(new Integer(value), target, method, "-1");
+        assertInvokeVirtualEquals(
+            new Integer(value), target, stdCM, "-1");
     }
 
     /**
@@ -181,14 +212,17 @@
      * then invokes the method.  If the returned value does not match 
      * 'value' then a test failure is indicated.
      */
-    public void assertInvokeInterfaceEquals(
-            Object value, Class target, Extends iface, PresentMethod method, 
-            String ... args) {
+    public void assertInvokeInterfaceEquals(Object value, Class target, 
+            Extends iface, PresentMethod method, String ... args) {
+
+        Compiler compiler = compilerLocal.get();
+        compiler.setVerbose(this.verbose);
+
         Class ii = invokeInterfaceHarness(target, iface, method, args);
-        Compiler compiler = new Compiler(this.verbose);
         ClassLoader loader = compiler.compile(ii, target);
 
         assertStaticCallEquals(loader, ii, method.getName(), value);
+        compiler.cleanup();
     }
 
     /**
@@ -197,9 +231,14 @@
      */
     public void assertInvokeInterfaceEquals(
             int value, Class target, Interface iface) {
-        PresentMethod m = new PresentMethod("int", stdMethodName);
+
+        Compiler compiler = compilerLocal.get();
+        compiler.setVerbose(this.verbose);
+
         assertInvokeInterfaceEquals(
-            new Integer(value), target, new Extends(iface), m);
+            new Integer(value), target, new Extends(iface), stdPM);
+
+        compiler.cleanup();
     }
 
     /**
@@ -211,8 +250,10 @@
     public void assertThrows(java.lang.Class<?> exceptionType, Class target, 
             ConcreteMethod method, String returns, String ... args) {
 
+        Compiler compiler = compilerLocal.get();
+        compiler.setVerbose(this.verbose);
+
         Class iv = invokeVirtualHarness(target, method, returns, args);
-        Compiler compiler = new Compiler(this.verbose);
         ClassLoader loader = compiler.compile(iv, target);
 
         java.lang.Class<?> cls = null;
@@ -236,6 +277,7 @@
             }
             assertEquals(e.getCause().getClass(), exceptionType);
         }
+        compiler.cleanup();
     }
 
     /**
@@ -243,7 +285,6 @@
      * a return type of 'int', and no arguments.
      */
     public void assertThrows(java.lang.Class<?> exceptionType, Class target) {
-        ConcreteMethod m = ConcreteMethod.returns("-1");
-        assertThrows(exceptionType, target, m, "-1");
+        assertThrows(exceptionType, target, stdCM, "-1");
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-ng/tests/org/openjdk/tests/shapegen/ClassCase.java	Thu Jul 12 13:07:55 2012 -0400
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.openjdk.tests.shapegen;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ *
+ * @author Robert Field
+ */
+public class ClassCase {
+    
+    public enum Kind {
+        IVAC        (true,  "v"),
+        IPRESENT    (true,  "p"),
+        IDEFAULT    (true,  "d"),
+        CNONE       (false, "n"),
+        CABSTRACT   (false, "a"),
+        CCONCRETE   (false, "c");
+        
+        private final String prefix;
+        public final boolean isInterface;
+        
+        Kind(boolean isInterface, String prefix) {
+            this.isInterface = isInterface;
+            this.prefix = prefix;
+        }
+
+        public String getPrefix() { return prefix; }
+    }
+    
+    public final Kind kind;
+    private final ClassCase superclass;
+    private final List<ClassCase> supertypes;
+    
+    private String name;
+    private boolean _OK;
+    private boolean _HasClassMethod;
+    private Set<ClassCase> _mprov;
+    private boolean _IsConcrete;
+    private boolean _HasDefault;
+    private ClassCase _mres;
+    private ClassCase _mdefend;
+    
+    private Set<RuleGroup> executed = new HashSet<RuleGroup>();
+    
+    public ClassCase(Kind kind, ClassCase superclass, List<ClassCase> interfaces) {
+        this.kind = kind;
+        this.superclass = superclass;
+        
+        // Set supertypes from superclass (if any) and interfaces
+        List<ClassCase> lc;
+        if (superclass == null) {
+            lc = interfaces;
+        } else {
+            lc = new ArrayList<>();
+            lc.add(superclass);
+            lc.addAll(interfaces);
+        }
+        this.supertypes = lc;
+    }
+    
+    public final boolean isInterface() { return kind.isInterface; }
+    public final boolean isClass() { return !kind.isInterface; }
+    
+    public Set<ClassCase> get_mprov() {
+        exec(RuleGroup.PROVENENCE);
+        return _mprov;
+    }
+    
+    public void set_mprov(ClassCase cc) {
+        Set<ClassCase> s = new HashSet<>();
+        s.add(cc);
+        _mprov = s;
+    }
+    
+    public void set_mprov(Set<ClassCase> s) {
+        _mprov = s;
+    }
+    
+    public ClassCase get_mres() {
+        exec(RuleGroup.RESOLUTION);
+        return _mres;
+    }
+    
+    public void set_mres(ClassCase cc) {
+        _mres = cc;
+    }
+    
+    public ClassCase get_mdefend() {
+        exec(RuleGroup.DEFENDER);
+        return _mdefend;
+    }
+    
+    public void set_mdefend(ClassCase cc) {
+        _mdefend = cc;
+    }
+    
+    public boolean get_HasClassMethod() {
+        exec(RuleGroup.PROVENENCE);
+        return _HasClassMethod;
+    }
+    
+    public void set_HasClassMethod(boolean bool) {
+        _HasClassMethod = bool;
+    }
+    
+    public boolean get_HasDefault() {
+        exec(RuleGroup.MARKER);
+        return _HasDefault;
+    }
+    
+    public void set_HasDefault(boolean bool) {
+        _HasDefault = bool;
+    }
+    
+    public boolean get_IsConcrete() {
+        exec(RuleGroup.MARKER);
+        return _IsConcrete;
+    }
+
+    public void set_IsConcrete(boolean bool) {
+        _IsConcrete = bool;
+    }
+    
+    public boolean get_OK() {
+        exec(RuleGroup.CHECKING);
+        return _OK;
+    }
+
+    public void set_OK(boolean bool) {
+        _OK = bool;
+    }
+    
+    public boolean isMethodDefined() {
+        for (ClassCase cc : supertypes) {
+            if (cc.isMethodDefined()) {
+                return true;
+            }
+        }
+        switch (kind) {
+            case CCONCRETE:
+            case CABSTRACT:
+            case IPRESENT:
+            case IDEFAULT:
+                return true;
+            default:
+                return false;
+        }
+    }
+    
+    public boolean isAbstract() {
+        return isMethodDefined() && (get_mres()==null);
+    }
+
+    public boolean hasSuperclass() {
+        return superclass != null;
+    }
+    
+    public ClassCase getSuperclass() {
+        return superclass;
+    }
+    
+    public List<ClassCase> getSupertypes() {
+        return supertypes;
+    }
+    
+    public List<ClassCase> getInterfaces() {
+        if (superclass != null) {
+            if (supertypes.get(0) != superclass) {
+                throw new AssertionError("superclass missing from supertypes");
+            }
+            return supertypes.subList(1, supertypes.size());
+        } else {
+            return supertypes;
+        }
+    }
+
+    public boolean isSubtypeOf(ClassCase cc) {
+        // S-Refl
+        if (cc.equals(this)) {
+            return true;
+        }
+
+        // S-Def
+        for (ClassCase sp : getSupertypes()) {
+            if (cc.equals(sp)) {
+                return true;
+            }
+        }
+
+        // _S-Trans
+        for (ClassCase sp : getSupertypes()) {
+            if (sp.isSubtypeOf(cc)) {
+                return true;
+            }
+        }
+        
+        return false;
+    }
+    
+    public void init(Map<String, Integer> namingContext) {
+        if (name != null) {
+            return; // Already inited
+        }
+
+        for (ClassCase sup : supertypes) {
+            sup.init(namingContext);
+        }
+
+        // Build name
+        StringBuilder sb = new StringBuilder();
+        if (!supertypes.isEmpty()) {
+            sb.append(isInterface() ? "I" : "C");
+            for (ClassCase cc : supertypes) {
+                sb.append(cc.getName());
+            }
+            sb.append(kind.isInterface ? "i" : "c");
+        }
+        sb.append(kind.prefix);
+        String pname = sb.toString();
+        Integer icnt = namingContext.get(pname);
+        int cnt = icnt == null ? 0 : icnt;
+        ++cnt;
+        namingContext.put(pname, cnt);
+        if (cnt > 1) {
+            sb.append(cnt);
+        }
+        this.name = sb.toString();
+    }
+    
+    public boolean isa(Kind... kinds) {
+        for (Kind k : kinds) {
+            if (kind == k) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    private void exec(RuleGroup rg ) {
+        if (!executed.contains(rg)) {
+            rg.exec(this);
+            executed.add(rg);
+        }
+    }
+    
+    public void collectClasses(Set<ClassCase> seen) {
+        seen.add(this);
+        for (ClassCase cc : supertypes) {
+            cc.collectClasses(seen);
+        }
+    }
+    
+    public String getID() {
+        if (name == null) {
+            throw new Error("Access to uninitialized ClassCase");
+        } else {
+            return name;
+        }
+    }
+
+    public final String getName() {
+        if (name == null) {
+            return "ClassCase uninited@" + hashCode();
+        } else {
+            return name;
+        }
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return obj instanceof ClassCase && getID().equals(((ClassCase)obj).getID());
+    }
+
+    @Override
+    public int hashCode() {
+        return getID().hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return getName();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-ng/tests/org/openjdk/tests/shapegen/Hierarchy.java	Thu Jul 12 13:07:55 2012 -0400
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.openjdk.tests.shapegen;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static org.openjdk.tests.shapegen.ClassCase.Kind.*;
+
+/**
+ *
+ * @author Robert Field
+ */
+public class Hierarchy {
+    
+    public final ClassCase root;
+    public final Set<ClassCase> all;
+    
+    public Hierarchy(ClassCase root) {
+        this.root = root;
+        root.init(new HashMap<String,Integer>());
+        Set<ClassCase> allClasses = new HashSet<>();
+        root.collectClasses(allClasses);
+        this.all = allClasses;
+    }
+    
+    public boolean anyDefaults() {
+        for (ClassCase cc : all) {
+            if (cc.kind == IDEFAULT) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    public boolean get_OK() {
+        return root.get_OK();
+    }
+    
+    public String testName() {
+        return root + "Test";
+    }
+
+    private static void genInterfaceList(StringBuilder buf, String prefix, List<ClassCase> interfaces) {
+            if (!interfaces.isEmpty()) {
+                buf.append(" ");
+                buf.append(prefix);
+                buf.append(" ");
+                buf.append(interfaces.get(0));
+                for (int i = 1; i < interfaces.size(); ++i) {
+                    buf.append(", " + interfaces.get(i));
+                }
+            }       
+    }
+    
+    public static void genClassDef(StringBuilder buf, ClassCase cc, String implClass, List<ClassCase> defaultRef) {
+        if (cc.isInterface()) {
+            buf.append("interface ");
+            buf.append(cc.getName() + " ");
+            genInterfaceList(buf, "extends", cc.getInterfaces());
+            buf.append(" {\n");
+            
+            switch (cc.kind) {
+                case IDEFAULT:
+                    buf.append("    String m() default { return \"\"; }\n");
+                    defaultRef.add(cc);
+                    break;
+                case IPRESENT:
+                    buf.append("    String m();\n");
+                    break;
+                case IVAC:
+                    break;
+                default:
+                    throw new AssertionError("Unexpected kind");
+            }
+            buf.append("}\n\n");
+        } else {
+            buf.append((cc.isAbstract()? "abstract " : ""));
+            buf.append(" class " + cc.getName());
+            if (cc.getSuperclass() != null) {
+                buf.append(" extends " + cc.getSuperclass());
+            }
+
+            genInterfaceList(buf, "implements", cc.getInterfaces());
+            buf.append(" {\n");
+            
+            switch (cc.kind) {
+                case CCONCRETE:
+                    buf.append("   public String m() { return \"\"; }\n");
+                    break;
+                case CABSTRACT:
+                    buf.append("   public abstract String m();\n");
+                    break;
+                case CNONE:
+                    break;
+                default:
+                    throw new AssertionError("Unexpected kind");
+            }
+            buf.append("}\n\n");
+        }
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return obj instanceof Hierarchy && root.getID().equals(((Hierarchy)obj).root.getID());
+    }
+
+    @Override
+    public int hashCode() {
+        return root.getID().hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return root.getName();
+    }
+
+    private static String classNames[] = {
+        "C", "D", "E", "F", "G", "H", "S", "T", "U", "V"
+    };
+
+    private static String interfaceNames[] = {
+        "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R"
+    };
+
+    private static int CLASS_INDEX = 0;
+    private static int INTERFACE_INDEX = 1;
+    private static int NUM_INDICIES = 2;
+
+    public List<String> getDescription() {
+        Map<ClassCase,String> nameMap = new HashMap<>();
+        assignNames(root, new int[NUM_INDICIES], nameMap);
+
+        ArrayList<String> res = new ArrayList<>();
+        if (root.getSupertypes().size() == 0) {
+           res.add(nameMap.get(root) + root.kind.getPrefix() + "()");    
+        } else {
+            genCaseDescription(root, res, new HashSet<ClassCase>(), nameMap);
+        }
+        return res;
+    }
+
+    private static void assignNames(
+            ClassCase cc, int indices[], Map<ClassCase,String> names) {
+        String name = names.get(cc);
+        if (name == null) {   
+            if (cc.isInterface()) {
+                names.put(cc, interfaceNames[indices[INTERFACE_INDEX]++]);
+            } else {
+                names.put(cc, classNames[indices[CLASS_INDEX]++]);
+            }  
+            for (int i = 0; i < cc.getSupertypes().size(); ++i) {
+                assignNames(cc.getSupertypes().get(i), indices, names);
+            }
+        }
+    }
+
+    private static void genCaseDescription(
+            ClassCase cc, List<String> res, Set<ClassCase> alreadyDone, 
+            Map<ClassCase,String> nameMap) {
+        if (!alreadyDone.contains(cc)) {
+            if (cc.getSupertypes().size() > 0) {
+                StringBuilder sb = new StringBuilder();
+                sb.append(nameMap.get(cc));
+                sb.append(cc.kind.getPrefix());
+                sb.append("(");
+                for (int i = 0; i < cc.getSupertypes().size(); ++i) {
+                    ClassCase supertype = cc.getSupertypes().get(i);
+                    if (i != 0) {
+                        sb.append(",");
+                    }
+                    genCaseDescription(supertype, res, alreadyDone, nameMap);
+                    sb.append(nameMap.get(supertype));
+                    sb.append(supertype.kind.getPrefix());
+                }
+                sb.append(")");
+                res.add(sb.toString());
+            }
+        }
+        alreadyDone.add(cc);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-ng/tests/org/openjdk/tests/shapegen/HierarchyGenerator.java	Thu Jul 12 13:07:55 2012 -0400
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.openjdk.tests.shapegen;
+
+import org.openjdk.tests.shapegen.ClassCase.Kind;
+
+import java.util.Collection;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Collections;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.openjdk.tests.shapegen.ClassCase.Kind.*;
+
+import static java.lang.Math.pow;
+
+/**
+ *
+ * @author Robert Field
+ */
+public final class HierarchyGenerator {  
+        
+    private int okcnt = 0;
+    private int errcnt = 0;
+    private Set<Hierarchy> uniqueOK = new HashSet<>();
+    private Set<Hierarchy> uniqueErr = new HashSet<>();
+
+    /**
+     * @param args the command line arguments
+     */
+    public HierarchyGenerator() {
+        organize("exhaustive interface", iExhaustive(2));
+        organize("exhaustive class", cExhaustive());
+        organize("shapes interface", iShapes());
+        organize("shapes class/interface", ciShapes());
+        
+        System.out.printf("\nExpect OK:    %d -- unique %d",   okcnt,  uniqueOK.size());
+        System.out.printf("\nExpect Error: %d -- unique %d\n", errcnt, uniqueErr.size());
+    }
+    
+    public Collection<Hierarchy> getOK() {
+        return uniqueOK;
+    }
+
+    public Collection<Hierarchy> getErr() {
+        return uniqueErr;
+    }
+
+    private void organize(String tname, List<Hierarchy> totest) {
+        System.out.printf("\nGenerating %s....\n", tname);
+        int nodefault = 0;
+        List<Hierarchy> ok = new ArrayList<>();
+        List<Hierarchy> err = new ArrayList<>();
+        for (Hierarchy cc : totest) {
+            if (cc.anyDefaults()) {
+                //System.out.printf("  %s\n", cc);
+                if (cc.get_OK()) {
+                    ok.add(cc);
+                } else {
+                    err.add(cc);
+                }
+            } else {
+                ++nodefault;
+            }
+        }
+        
+        errcnt += err.size();
+        okcnt += ok.size();
+        uniqueErr.addAll(err);
+        uniqueOK.addAll(ok);
+        
+        System.out.printf("  %5d No default\n  %5d Error\n  %5d OK\n  %5d Total\n", 
+                nodefault, err.size(), ok.size(), totest.size());
+    }
+    
+    public List<Hierarchy> iExhaustive(int idepth) {
+        List<ClassCase> current = new ArrayList<>();
+        for (int i = 0; i < idepth; ++i) {
+            current = ilayer(current);
+        }
+        return wrapInClassAndHierarchy(current);
+    }
+    
+    private List<ClassCase> ilayer(List<ClassCase> srcLayer) {
+        List<ClassCase> lay = new ArrayList<>();
+        for (int i = (int) pow(2, srcLayer.size()) - 1; i >= 0; --i) {
+            List<ClassCase> itfs = new ArrayList<>();
+            for (int b = srcLayer.size() - 1; b >= 0; --b) {
+                if ((i & (1<<b)) != 0) {
+                    itfs.add(srcLayer.get(b));
+                }
+            }
+            lay.add(new ClassCase(IVAC, null, itfs));
+            lay.add(new ClassCase(IPRESENT, null, itfs));
+            lay.add(new ClassCase(IDEFAULT, null, itfs));
+            lay.add(new ClassCase(IDEFAULT, null, itfs));
+        }
+        return lay;
+    }
+    
+    public List<Hierarchy> cExhaustive() {
+        final Kind[] iKinds = new Kind[]{IDEFAULT, IVAC, IPRESENT, null};
+        final Kind[] cKinds = new Kind[]{CNONE, CABSTRACT, CCONCRETE};
+        List<Hierarchy> totest = new ArrayList<>();
+        for (int i1 = 0; i1 < iKinds.length; ++i1) {
+            for (int i2 = 0; i2 < iKinds.length; ++i2) {
+                for (int i3 = 0; i3 < iKinds.length; ++i3) {
+                    for (int c1 = 0; c1 < cKinds.length; ++c1) {
+                        for (int c2 = 0; c2 < cKinds.length; ++c2) {
+                            for (int c3 = 0; c3 < cKinds.length; ++c3) {
+                                totest.add( new Hierarchy(
+                                        new ClassCase(cKinds[c1],
+                                            new ClassCase(cKinds[c2],
+                                                new ClassCase(cKinds[c3],
+                                                    null,
+                                                    iList(iKinds[i1])
+                                                ),
+                                                iList(iKinds[i2])
+                                            ),
+                                            iList(iKinds[i3])
+                                        )));
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return totest;
+    }
+
+    public static final List<ClassCase> EMPTY_LIST = new ArrayList<>();
+    
+    private List<ClassCase> iList(Kind kind) {
+        if (kind == null) {
+            return EMPTY_LIST;
+        } else {
+            List<ClassCase> itfs = new ArrayList<>();
+            itfs.add(new ClassCase(kind, null, EMPTY_LIST));
+            return itfs;
+        }
+    }
+    
+    public List<Hierarchy> ciShapes() {
+        return wrapInHierarchy(TTShape.allCases(true));
+    }
+
+    public List<Hierarchy> iShapes() {
+        return wrapInClassAndHierarchy(TTShape.allCases(false));
+    }
+
+    public List<Hierarchy> wrapInClassAndHierarchy(List<ClassCase> ihs) {
+        List<Hierarchy> totest = new ArrayList<>();
+        for (ClassCase cc : ihs) {
+            List<ClassCase> interfaces = new ArrayList<>();
+            interfaces.add(cc);
+            totest.add(new Hierarchy(new ClassCase(CNONE, null, interfaces)));
+        }
+        return totest;
+    }
+
+    public List<Hierarchy> wrapInHierarchy(List<ClassCase> ihs) {
+        List<Hierarchy> totest = new ArrayList<>();
+        for (ClassCase cc : ihs) {
+            totest.add(new Hierarchy(cc));
+        }
+        return totest;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-ng/tests/org/openjdk/tests/shapegen/Rule.java	Thu Jul 12 13:07:55 2012 -0400
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.openjdk.tests.shapegen;
+
+/**
+ *
+ * @author Robert Field
+ */
+public abstract class Rule {
+
+    public final String name;
+
+    public Rule(String name) {
+        this.name = name;
+    }
+
+    abstract boolean guard(ClassCase cc);
+
+    abstract void eval(ClassCase cc);
+    
+    @Override
+    public String toString() {
+        return name;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-ng/tests/org/openjdk/tests/shapegen/RuleGroup.java	Thu Jul 12 13:07:55 2012 -0400
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.openjdk.tests.shapegen;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import static org.openjdk.tests.shapegen.ClassCase.Kind.*;
+
+/**
+ *
+ * @author Robert Field
+ */
+public class RuleGroup {
+
+    final String name;
+    private final Rule[] rules;
+    
+    public RuleGroup(String name, Rule[] rules) {
+        this.name = name;
+        this.rules = rules;
+    }
+    
+    public boolean exec(ClassCase cc) {
+        boolean found = false;
+        for (Rule rule : rules) {
+            if (rule.guard(cc)) {
+                if (found) {
+                    throw new RuntimeException("Bad rules -- multiple matches " + toString() + " for " + cc);
+                } else {
+                    rule.eval(cc);
+                    found = true;
+                }
+            }
+        }
+        return found;
+    }
+    
+    @Override
+    public String toString() {
+        return name;
+    }
+    
+    public static RuleGroup PROVENENCE = new RuleGroup("Provenence", new Rule[] {
+      new Rule("P-CDeclare") {
+          boolean guard(ClassCase cc) {
+              return cc.isa(CCONCRETE, CABSTRACT);
+          }
+
+          void eval(ClassCase cc) {
+              cc.set_mprov(cc);
+              cc.set_HasClassMethod(true);
+          }
+      },
+      
+      new Rule("P-IDeclare") {
+          boolean guard(ClassCase cc) {
+              return cc.isa(IDEFAULT, IPRESENT);
+          }
+
+          void eval(ClassCase cc) {
+              cc.set_mprov(cc);
+          }
+      },
+      
+      new Rule("P-IntfInh") {
+          boolean guard(ClassCase cc) {
+              return cc.isa(IVAC, CNONE) && !(cc.hasSuperclass() && cc.getSuperclass().get_HasClassMethod());
+          }
+
+          void eval(ClassCase cc) {
+              Set<ClassCase> _S = new HashSet<>();
+              for (ClassCase t : cc.getSupertypes()) {
+                  _S.addAll(t.get_mprov());
+              }
+              Set<ClassCase> tops = new HashSet<>();
+              for (ClassCase _W : _S) {
+                  for (ClassCase _V : _S) {
+                      if (_V.equals(_W) || !(_V.isSubtypeOf(_W))) {
+                          tops.add(_W);
+                      }
+                  }
+              }
+              cc.set_mprov(tops);
+          }
+      },
+      
+      new Rule("P-ClassInh") {
+          boolean guard(ClassCase cc) {
+              return cc.isa(CNONE) && (cc.hasSuperclass() && cc.getSuperclass().get_HasClassMethod());
+          }
+
+          void eval(ClassCase cc) {
+              cc.set_mprov(cc.getSuperclass());
+              cc.set_HasClassMethod(true);
+          }
+      },
+      
+    });
+
+    public static RuleGroup MARKER = new RuleGroup("Marker", new Rule[] {
+      new Rule("M-Default") {
+          boolean guard(ClassCase cc) {
+              return cc.isa(IDEFAULT);
+          }
+
+          void eval(ClassCase cc) {
+              cc.set_HasDefault(true);
+          }
+      },
+
+      new Rule("M-Conc") {
+          boolean guard(ClassCase cc) {
+            return cc.isa(CCONCRETE);
+          }
+
+          void eval(ClassCase cc) {
+              cc.set_IsConcrete(true);
+          }
+      },
+      
+    });
+
+    public static RuleGroup RESOLUTION = new RuleGroup("Resolution", new Rule[] {
+      new Rule("R-Resolve") {
+          boolean guard(ClassCase cc) {
+              if (!(cc.isClass() && cc.get_mprov().size() == 1)) {
+                  return false;
+              }
+              ClassCase _V = cc.get_mprov().iterator().next();
+              return _V.get_IsConcrete() || _V.get_HasDefault();
+          }
+
+          void eval(ClassCase cc) {
+              ClassCase _V = cc.get_mprov().iterator().next();
+              cc.set_mres(_V);
+          }
+      },
+      
+    });
+
+    public static RuleGroup DEFENDER = new RuleGroup("Defender", new Rule[] {
+      new Rule("D-Defend") {
+          boolean guard(ClassCase cc) {
+              ClassCase mresSuper = cc.hasSuperclass() ? cc.getSuperclass().get_mres() : null;
+              boolean eq = cc.get_mres() == null ? mresSuper == null : cc.get_mres().equals(mresSuper);
+              return cc.isa(CNONE) && !eq;
+          }
+
+          void eval(ClassCase cc) {
+              cc.set_mdefend(cc.get_mres());
+          }
+      },
+      
+    });
+
+    public static RuleGroup CHECKING = new RuleGroup("Checking", new Rule[] {
+      new Rule("C-Check") {
+          boolean guard(ClassCase cc) {
+              for (ClassCase t : cc.getSupertypes()) {
+                  if (! t.get_OK()) {
+                      return false;
+                  }
+              }
+              int defenderCount = 0;
+              int provCount = 0;
+              for (ClassCase prov : cc.get_mprov()) {
+                  if (prov.get_HasDefault()) {
+                      defenderCount++;
+                  }
+                  provCount++;
+              }
+              return provCount <= 1 || defenderCount == 0;
+          }
+
+          void eval(ClassCase cc) {
+              cc.set_OK(true);
+          }
+      },
+      
+    });
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-ng/tests/org/openjdk/tests/shapegen/TTNode.java	Thu Jul 12 13:07:55 2012 -0400
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.openjdk.tests.shapegen;
+
+import org.openjdk.tests.shapegen.ClassCase.Kind;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import static org.openjdk.tests.shapegen.ClassCase.Kind.*;
+
+/**
+ * Type Template Node
+ * 
+ * @author Robert Field
+ */
+public class TTNode {
+    
+    final List<TTNode> supertypes;
+    final boolean canBeClass;
+    
+    private int currentKindIndex;
+    private Kind[] kinds;
+    
+    public TTNode(List<TTNode> subtypes, boolean canBeClass) {
+        this.supertypes = subtypes;
+        this.canBeClass = canBeClass;
+    }
+    
+    public void start(boolean includeClasses) {
+        kinds = 
+             supertypes.isEmpty()?
+                (new Kind[]{IDEFAULT, IPRESENT})
+             :  ((includeClasses && canBeClass)?
+                  new Kind[]{CNONE, IVAC, IDEFAULT, IPRESENT}
+                : new Kind[]{IVAC, IDEFAULT, IPRESENT});
+        currentKindIndex = 0;
+        
+        for (TTNode sub : supertypes) {
+            sub.start(includeClasses);
+        }
+    }
+    
+    public boolean next() {
+        ++currentKindIndex;
+        if (currentKindIndex >= kinds.length) {
+            currentKindIndex = 0;
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    public void collectAllSubtypes(Set<TTNode> subs) {
+        subs.add(this);
+        for (TTNode n : supertypes) {
+            n.collectAllSubtypes(subs);
+        }
+    }
+
+    private Kind getKind() {
+        return kinds[currentKindIndex];
+    }
+    
+    boolean isInterface() {
+        return getKind().isInterface;
+    }
+    
+    boolean isClass() {
+        return !isInterface();
+    }
+    
+    boolean hasDefault() {
+        return getKind() == IDEFAULT;
+    }
+    
+    public boolean isValid() {
+        for (TTNode n : supertypes) {
+            if (!n.isValid() || (isInterface() && n.isClass())) {
+                return false;
+            }
+        }
+        return true;
+    }
+    
+    public ClassCase genCase() {
+        ClassCase subclass;
+        List<TTNode> ttintfs;
+        if (isClass() && !supertypes.isEmpty() && supertypes.get(0).isClass()) {
+            subclass = supertypes.get(0).genCase();
+            ttintfs = supertypes.subList(1, supertypes.size());
+        } else {
+            subclass = null;
+            ttintfs = supertypes;
+        }
+        List<ClassCase> intfs = new ArrayList<>();
+        for (TTNode node : ttintfs) {
+            intfs.add(node.genCase());
+        }
+        return new ClassCase(getKind(), subclass, intfs);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-ng/tests/org/openjdk/tests/shapegen/TTParser.java	Thu Jul 12 13:07:55 2012 -0400
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.openjdk.tests.shapegen;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.io.IOException;
+import java.io.StringReader;
+
+import static java.lang.Character.isLetter;
+import static java.lang.Character.isUpperCase;
+import static java.lang.Character.isWhitespace;
+
+/**
+ * Parse a type template definition string
+ * 
+ *   input     :: classDef
+ *   classDef  :: letter [ ( classDef* ) ]
+ * 
+ * @author Robert Field
+ */
+public class TTParser extends StringReader {
+
+    private Map<Character, TTNode> letterMap = new HashMap<>();
+    private char ch;
+    
+    private final String def;
+
+    public TTParser(String s) {
+        super(s);
+        this.def = s;
+    }
+
+    private void advance() throws IOException {
+        do {
+            ch = (char)read();
+        } while (isWhitespace(ch));
+    }
+
+    public TTNode parse() {
+        try {
+            advance();
+            return classDef();
+        } catch (IOException t) {
+            throw new RuntimeException(t);
+        }
+    }
+
+    private TTNode classDef() throws IOException {
+        if (!isLetter(ch)) {
+            if (ch == (char)-1) {
+                throw new IOException("Unexpected end of type template in " + def);
+            } else {
+                throw new IOException("Unexpected character in type template: " + (Character)ch + " in " + def);
+            }
+        }
+        char nodeCh = ch;
+        TTNode node = letterMap.get(nodeCh);
+        boolean canBeClass = isUpperCase(nodeCh);
+        advance();
+        if (node == null) {
+            List<TTNode> subtypes = new ArrayList<>();
+            if (ch == '(') {
+                advance();
+                while (ch != ')') {
+                    subtypes.add(classDef());
+                }
+                advance();
+            }
+            node = new TTNode(subtypes, canBeClass);
+            letterMap.put(nodeCh, node);
+        }
+        return node;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-ng/tests/org/openjdk/tests/shapegen/TTShape.java	Thu Jul 12 13:07:55 2012 -0400
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.openjdk.tests.shapegen;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ *
+ * @author Robert Field
+ */
+public class TTShape {
+    
+    private final TTNode root;
+    private final TTNode[] nodes;
+    
+    TTShape(TTNode root) {
+        this.root = root;
+        Set<TTNode> subs = new HashSet<>();
+        root.collectAllSubtypes(subs);
+        nodes = subs.toArray(new TTNode[subs.size()]);
+    }
+    
+    private List<ClassCase> toCases(boolean includeClasses) {
+        List<ClassCase> ccs = new ArrayList<>();
+        root.start(includeClasses);
+        int i;
+        outer:
+        while (true) {
+            if (root.isValid()) {
+                ClassCase cc = root.genCase();
+                //System.out.println(cc);
+                ccs.add(cc);
+            }
+            
+            i = 0; 
+            do {
+                if (i >= nodes.length) {
+                    break outer;
+                }
+            } while(!nodes[i++].next());
+        }
+        return ccs;
+    }
+
+   public static List<ClassCase> allCases(boolean includeClasses) {
+        List<ClassCase> ccs = new ArrayList<>();
+        for (TTShape shape : SHAPES) {
+            ccs.addAll(shape.toCases(includeClasses));
+        }
+        return ccs;
+    }
+    
+    public static TTShape parse(String s) {
+        return new TTShape(new TTParser(s).parse());
+    }
+    
+    public static final TTShape[] SHAPES = new TTShape[] {
+        parse("a"),
+        parse("a(b)"),
+        parse("A(bb)"),
+        parse("A(B(d)c(d))"),
+        parse("A(b(c))"),
+        parse("A(B(cd)d)"),
+        parse("A(B(c)c)"),
+        parse("A(B(Ce)d(e))"),
+        parse("A(B(C)d(e))"),
+        parse("A(Bc(d))"),
+        parse("A(B(d)dc)"),
+        parse("A(B(dc)dc)"),
+        parse("A(B(c(d))d)"),
+        parse("A(B(C(d))d)"),
+        parse("A(B(C(e)d(e))e)"),
+        parse("A(B(c(d))c)"),
+        parse("A(B(dc(d))c)"),
+        parse("A(B(C(d))d)"),
+    };
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-ng/tests/org/openjdk/tests/vm/FDSeparateCompilationTest.java	Thu Jul 12 13:07:55 2012 -0400
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.openjdk.tests.vm;
+
+import java.lang.reflect.*;
+import java.util.*;
+import java.io.File;
+import java.io.IOException;
+
+import org.testng.ITestResult;
+import org.testng.annotations.Test;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.AfterMethod;
+
+import org.openjdk.tests.separate.*;
+import org.openjdk.tests.separate.Compiler;
+
+import org.openjdk.tests.shapegen.Hierarchy;
+import org.openjdk.tests.shapegen.HierarchyGenerator;
+import org.openjdk.tests.shapegen.ClassCase;
+
+import static org.testng.Assert.*;
+import static org.openjdk.tests.separate.SourceModel.*;
+import static org.openjdk.tests.separate.SourceModel.Class;
+import static org.openjdk.tests.separate.SourceModel.Method;
+import static org.openjdk.tests.separate.SourceModel.Type;
+
+public class FDSeparateCompilationTest extends TestHarness {
+
+    private static String EMPTY = "\"\"";
+
+    @DataProvider(name = "allShapes", parallel = true)
+    public Object[][] hierarchyGenerator() {
+        ArrayList<Object[]> allCases = new ArrayList<>();
+
+        HierarchyGenerator hg = new HierarchyGenerator();
+        hg.getOK().map(x -> new Object[] { x }).into(allCases);
+        hg.getErr().map(x -> new Object[] { x }).into(allCases);
+        return allCases.toArray(new Object[0][]);
+    }
+
+    // The expected value obtained when invoking the method from the specified 
+    // class.  If returns null, then an AbstractMethodError is expected.
+    private static String getExpectedResult(ClassCase cc) {
+        Set<ClassCase> provs = cc.get_mprov();
+        if (cc.get_mres() != null) {
+            return cc.get_mres().getName();
+        } else if (provs != null && provs.size() == 1) {
+            ClassCase cand = provs.getOnly();
+            switch (cand.kind) {
+                case CCONCRETE:
+                case IDEFAULT:
+                    return cand.getName();
+                case CNONE:
+                case IVAC:
+                    return getExpectedResult(cand);
+            }
+        } 
+        return null;
+    }
+
+    private static final ConcreteMethod canonicalMethod = new ConcreteMethod(
+            "String", "m", "returns " + EMPTY + ";");
+
+    @Test(groups = "vm", dataProvider = "allShapes")
+    public void separateCompilationTest(Hierarchy hs) {
+        ClassCase cc = hs.root;
+        Type type = sourceTypeFrom(hs.root);
+
+        Class specimen = null;
+        if (type instanceof Class) {
+            Class ctype = (Class)type;
+            if (ctype.getIsAbstract()) {
+                specimen = new Class("S", ctype);
+            } else {
+                specimen = ctype;
+            }
+        } else {
+            specimen = new Class("S", (Interface)type);
+        }
+
+        String value = getExpectedResult(cc);
+        if (value != null) {
+            assertInvokeVirtualEquals(value, specimen, canonicalMethod, EMPTY);
+        } else {
+            assertThrows(AbstractMethodError.class, specimen, 
+                canonicalMethod, EMPTY);
+        }
+    }
+
+    @AfterMethod 
+    public void printCaseError(ITestResult result) {
+        if (result.getStatus() == ITestResult.FAILURE) {
+            Hierarchy hs = (Hierarchy)result.getParameters()[0];
+            System.out.println("Separate compilation case " + hs);
+            printCaseDetails(hs);
+        }
+    }
+
+    private void printCaseDetails(Hierarchy hs) {
+        String exp = getExpectedResult(hs.root);
+        hs.getDescription().forEach(
+                x -> { System.out.println("    " + x); });
+        if (exp != null) { 
+            System.out.println("    Expected \"" + exp + "\"");
+        } else {
+            System.out.println("    Expected AbstractMethodError");
+        }
+    }
+
+    private Type sourceTypeFrom(ClassCase cc) {
+        Type type = null;
+
+        if (cc.isInterface()) {
+            Interface iface = new Interface(cc.getName());
+            for (ClassCase scc : cc.getInterfaces()) {
+                Interface supertype = (Interface)sourceTypeFrom(scc);
+                iface.addSuperType(supertype);
+            }
+            type = iface;
+        } else {
+            Class cls = new Class(cc.getName());
+            if (cc.hasSuperclass()) {
+                Class superc = (Class)sourceTypeFrom(cc.getSuperclass());
+                cls.setSuperClass(superc);
+            }
+            for (ClassCase scc : cc.getInterfaces()) {
+                Interface supertype = (Interface)sourceTypeFrom(scc);
+                cls.addSuperType(supertype);
+            }
+            if (cc.isAbstract()) { 
+                cls.setIsAbstract(true);  
+            }
+            type = cls;
+        }
+        Method method = methodFrom(cc);
+        if (method != null) {
+            type.addMethod(method);
+        }
+        return type;
+    }
+
+    private Method methodFrom(ClassCase cc) {
+        switch (cc.kind) {
+            case IVAC: 
+            case CNONE: return null; 
+            case IPRESENT: 
+                return new PresentMethod("String", "m"); 
+            case IDEFAULT:
+                return new DefaultMethod(
+                    "String", "m", "return \"" + cc.getName() + "\";");
+            case CABSTRACT:
+                return new AbstractMethod("String", "m"); 
+            case CCONCRETE:
+                return new ConcreteMethod(
+                    "String", "m", "return \"" + cc.getName() + "\";");
+            default:
+                fail("Unknown method type in class");
+                return null;
+        }
+    }
+}