8015322: Javac template test framework
authoremc
Mon, 09 Sep 2013 17:11:55 -0400
changeset 19931 f82b95ab8015
parent 19930 b6181c0049f3
child 19932 1386f12ad4b9
8015322: Javac template test framework Summary: Putback of the javac template test framework from the Lambda repository Reviewed-by: jjg Contributed-by: brian.goetz@oracle.com
langtools/README
langtools/test/lib/combo/TEST.properties
langtools/test/lib/combo/tools/javac/combo/Diagnostics.java
langtools/test/lib/combo/tools/javac/combo/JavacTemplateTestBase.java
langtools/test/lib/combo/tools/javac/combo/Template.java
langtools/test/lib/combo/tools/javac/combo/TemplateTest.java
langtools/test/tools/javac/lambda/bridge/template_tests/BridgeMethodTestCase.java
langtools/test/tools/javac/lambda/bridge/template_tests/BridgeMethodsTemplateTest.java
langtools/test/tools/javac/lambda/bridge/template_tests/TEST.properties
--- a/langtools/README	Mon Sep 09 16:26:55 2013 -0400
+++ b/langtools/README	Mon Sep 09 17:11:55 2013 -0400
@@ -32,7 +32,7 @@
 JLS and JVMS.
 
 In addition, there is a substantial collection of regression and unit
-tests for all the tools in the maain langtools test/ directory.
+tests for all the tools in the main langtools test/ directory.
 
 Finally, there is a small set of tests to do basic validation of a build
 of the langtools workspace for use by JDK. These tests check the contents
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/lib/combo/TEST.properties	Mon Sep 09 17:11:55 2013 -0400
@@ -0,0 +1,4 @@
+# This file identifies root(s) of the test-ng hierarchy.
+
+
+TestNG.dirs = .
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/lib/combo/tools/javac/combo/Diagnostics.java	Mon Sep 09 17:11:55 2013 -0400
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2013, 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 tools.javac.combo;
+
+import javax.tools.Diagnostic;
+import javax.tools.JavaFileObject;
+import java.util.ArrayList;
+import java.util.List;
+
+import static java.util.stream.Collectors.toList;
+
+/**
+* A container for compiler diagnostics, separated into errors and warnings,
+ * used by JavacTemplateTestBase.
+ *
+ * @author Brian Goetz
+*/
+public class Diagnostics implements javax.tools.DiagnosticListener<JavaFileObject> {
+
+    protected List<Diagnostic<? extends JavaFileObject>> diags = new ArrayList<>();
+    protected boolean foundErrors = false;
+
+    public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+        diags.add(diagnostic);
+        foundErrors = foundErrors || diagnostic.getKind() == Diagnostic.Kind.ERROR;
+    }
+
+    /** Were there any errors found? */
+    public boolean errorsFound() {
+        return foundErrors;
+    }
+
+    /** Get all diagnostic keys */
+    public List<String> keys() {
+        return diags.stream()
+                    .map(Diagnostic::getCode)
+                    .collect(toList());
+    }
+
+    /** Do the diagnostics contain the specified error key? */
+    public boolean containsErrorKey(String key) {
+        return diags.stream()
+                    .filter(d -> d.getKind() == Diagnostic.Kind.ERROR)
+                    .anyMatch(d -> d.getCode().equals(key));
+    }
+
+    /** Get the error keys */
+    public List<String> errorKeys() {
+        return diags.stream()
+                    .filter(d -> d.getKind() == Diagnostic.Kind.ERROR)
+                    .map(Diagnostic::getCode)
+                    .collect(toList());
+    }
+
+    public String toString() { return keys().toString(); }
+
+    /** Clear all diagnostic state */
+    public void reset() {
+        diags.clear();
+        foundErrors = false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/lib/combo/tools/javac/combo/JavacTemplateTestBase.java	Mon Sep 09 17:11:55 2013 -0400
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2013, 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 tools.javac.combo;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+
+import com.sun.source.util.JavacTask;
+import com.sun.tools.javac.util.Pair;
+import org.testng.ITestResult;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.AfterSuite;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.fail;
+
+/**
+ * Base class for template-driven TestNG javac tests that support on-the-fly
+ * source file generation, compilation, classloading, execution, and separate
+ * compilation.
+ *
+ * <p>Manages a set of templates (which have embedded tags of the form
+ * {@code #\{NAME\}}), source files (which are also templates), and compile
+ * options.  Test cases can register templates and source files, cause them to
+ * be compiled, validate whether the set of diagnostic messages output by the
+ * compiler is correct, and optionally load and run the compiled classes.
+ *
+ * @author Brian Goetz
+ */
+@Test
+public abstract class JavacTemplateTestBase {
+    private static final Set<String> suiteErrors = Collections.synchronizedSet(new HashSet<>());
+    private static final AtomicInteger counter = new AtomicInteger();
+    private static final File root = new File("gen");
+    private static final File nullDir = new File("empty");
+
+    protected final Map<String, Template> templates = new HashMap<>();
+    protected final Diagnostics diags = new Diagnostics();
+    protected final List<Pair<String, Template>> sourceFiles = new ArrayList<>();
+    protected final List<String> compileOptions = new ArrayList<>();
+    protected final List<File> classpaths = new ArrayList<>();
+    protected final Template.Resolver defaultResolver = new MapResolver(templates);
+
+    private Template.Resolver currentResolver = defaultResolver;
+
+    /** Add a template with a specified name */
+    protected void addTemplate(String name, Template t) {
+        templates.put(name, t);
+    }
+
+    /** Add a template with a specified name */
+    protected void addTemplate(String name, String s) {
+        templates.put(name, new StringTemplate(s));
+    }
+
+    /** Add a source file */
+    protected void addSourceFile(String name, Template t) {
+        sourceFiles.add(new Pair<>(name, t));
+    }
+
+    /** Add a File to the class path to be used when loading classes; File values
+     * will generally be the result of a previous call to {@link #compile()}.
+     * This enables testing of separate compilation scenarios if the class path
+     * is set up properly.
+     */
+    protected void addClassPath(File path) {
+        classpaths.add(path);
+    }
+
+    /**
+     * Add a set of compilation command-line options
+     */
+    protected void addCompileOptions(String... opts) {
+        Collections.addAll(compileOptions, opts);
+    }
+
+    /** Reset the compile options to the default (empty) value */
+    protected void resetCompileOptions() { compileOptions.clear(); }
+
+    /** Remove all templates */
+    protected void resetTemplates() { templates.clear(); }
+
+    /** Remove accumulated diagnostics */
+    protected void resetDiagnostics() { diags.reset(); }
+
+    /** Remove all source files */
+    protected void resetSourceFiles() { sourceFiles.clear(); }
+
+    /** Remove registered class paths */
+    protected void resetClassPaths() { classpaths.clear(); }
+
+    // Before each test method, reset everything
+    @BeforeMethod
+    public void reset() {
+        resetCompileOptions();
+        resetDiagnostics();
+        resetSourceFiles();
+        resetTemplates();
+        resetClassPaths();
+    }
+
+    // After each test method, if the test failed, capture source files and diagnostics and put them in the log
+    @AfterMethod
+    public void copyErrors(ITestResult result) {
+        if (!result.isSuccess()) {
+            suiteErrors.addAll(diags.errorKeys());
+
+            List<Object> list = new ArrayList<>();
+            Collections.addAll(list, result.getParameters());
+            list.add("Test case: " + getTestCaseDescription());
+            for (Pair<String, Template> e : sourceFiles)
+                list.add("Source file " + e.fst + ": " + e.snd);
+            if (diags.errorsFound())
+                list.add("Compile diagnostics: " + diags.toString());
+            result.setParameters(list.toArray(new Object[list.size()]));
+        }
+    }
+
+    @AfterSuite
+    // After the suite is done, dump any errors to output
+    public void dumpErrors() {
+        if (!suiteErrors.isEmpty())
+            System.err.println("Errors found in test suite: " + suiteErrors);
+    }
+
+    /**
+     * Get a description of this test case; since test cases may be combinatorially
+     * generated, this should include all information needed to describe the test case
+     */
+    protected String getTestCaseDescription() {
+        return this.toString();
+    }
+
+    /** Assert that all previous calls to compile() succeeded */
+    protected void assertCompileSucceeded() {
+        if (diags.errorsFound())
+            fail("Expected successful compilation");
+    }
+
+    /**
+     * If the provided boolean is true, assert all previous compiles succeeded,
+     * otherwise assert that a compile failed.
+     * */
+    protected void assertCompileSucceededIff(boolean b) {
+        if (b)
+            assertCompileSucceeded();
+        else
+            assertCompileFailed();
+    }
+
+    /** Assert that a previous call to compile() failed */
+    protected void assertCompileFailed() {
+        if (!diags.errorsFound())
+            fail("Expected failed compilation");
+    }
+
+    /** Assert that a previous call to compile() failed with a specific error key */
+    protected void assertCompileFailed(String message) {
+        if (!diags.errorsFound())
+            fail("Expected failed compilation: " + message);
+    }
+
+    /** Assert that a previous call to compile() failed with all of the specified error keys */
+    protected void assertCompileErrors(String... keys) {
+        if (!diags.errorsFound())
+            fail("Expected failed compilation");
+        for (String k : keys)
+            if (!diags.containsErrorKey(k))
+                fail("Expected compilation error " + k);
+    }
+
+    /** Convert an object, which may be a Template or a String, into a Template */
+    protected Template asTemplate(Object o) {
+        if (o instanceof Template)
+            return (Template) o;
+        else if (o instanceof String)
+            return new StringTemplate((String) o);
+        else
+            return new StringTemplate(o.toString());
+    }
+
+    /** Compile all registered source files */
+    protected void compile() throws IOException {
+        compile(false);
+    }
+
+    /** Compile all registered source files, optionally generating class files
+     * and returning a File describing the directory to which they were written */
+    protected File compile(boolean generate) throws IOException {
+        List<JavaFileObject> files = new ArrayList<>();
+        for (Pair<String, Template> e : sourceFiles)
+            files.add(new FileAdapter(e.fst, asTemplate(e.snd)));
+        return compile(classpaths, files, generate);
+    }
+
+    /** Compile all registered source files, using the provided list of class paths
+     * for finding required classfiles, optionally generating class files
+     * and returning a File describing the directory to which they were written */
+    protected File compile(List<File> classpaths, boolean generate) throws IOException {
+        List<JavaFileObject> files = new ArrayList<>();
+        for (Pair<String, Template> e : sourceFiles)
+            files.add(new FileAdapter(e.fst, asTemplate(e.snd)));
+        return compile(classpaths, files, generate);
+    }
+
+    private File compile(List<File> classpaths, List<JavaFileObject> files, boolean generate) throws IOException {
+        JavaCompiler systemJavaCompiler = ToolProvider.getSystemJavaCompiler();
+        StandardJavaFileManager fm = systemJavaCompiler.getStandardFileManager(null, null, null);
+        if (classpaths.size() > 0)
+            fm.setLocation(StandardLocation.CLASS_PATH, classpaths);
+        JavacTask ct = (JavacTask) systemJavaCompiler.getTask(null, fm, diags, compileOptions, null, files);
+        if (generate) {
+            File destDir = new File(root, Integer.toString(counter.incrementAndGet()));
+            // @@@ Assert that this directory didn't exist, or start counter at max+1
+            destDir.mkdirs();
+            fm.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(destDir));
+            ct.generate();
+            return destDir;
+        }
+        else {
+            ct.analyze();
+            return nullDir;
+        }
+    }
+
+    /** Load the given class using the provided list of class paths */
+    protected Class<?> loadClass(String className, File... destDirs) {
+        try {
+            List<URL> list = new ArrayList<>();
+            for (File f : destDirs)
+                list.add(new URL("file:" + f.toString().replace("\\", "/") + "/"));
+            return Class.forName(className, true, new URLClassLoader(list.toArray(new URL[list.size()])));
+        } catch (ClassNotFoundException | MalformedURLException e) {
+            throw new RuntimeException("Error loading class " + className, e);
+        }
+    }
+
+    /** An implementation of Template which is backed by a String */
+    protected class StringTemplate implements Template {
+        protected final String template;
+
+        public StringTemplate(String template) {
+            this.template = template;
+        }
+
+        public String expand(String selector) {
+            return Behavior.expandTemplate(template, currentResolver);
+        }
+
+        public String toString() {
+            return expand("");
+        }
+
+        public StringTemplate with(final String key, final String value) {
+            return new StringTemplateWithResolver(template, new KeyResolver(key, value));
+        }
+
+    }
+
+    /** An implementation of Template which is backed by a String and which
+     * encapsulates a Resolver for resolving embedded tags. */
+    protected class StringTemplateWithResolver extends StringTemplate {
+        private final Resolver localResolver;
+
+        public StringTemplateWithResolver(String template, Resolver localResolver) {
+            super(template);
+            this.localResolver = localResolver;
+        }
+
+        @Override
+        public String expand(String selector) {
+            Resolver saved = currentResolver;
+            currentResolver = new ChainedResolver(currentResolver, localResolver);
+            try {
+                return super.expand(selector);
+            }
+            finally {
+                currentResolver = saved;
+            }
+        }
+
+        @Override
+        public StringTemplate with(String key, String value) {
+            return new StringTemplateWithResolver(template, new ChainedResolver(localResolver, new KeyResolver(key, value)));
+        }
+    }
+
+    /** A Resolver which uses a Map to resolve tags */
+    private class KeyResolver implements Template.Resolver {
+        private final String key;
+        private final String value;
+
+        public KeyResolver(String key, String value) {
+            this.key = key;
+            this.value = value;
+        }
+
+        @Override
+        public Template lookup(String k) {
+            return key.equals(k) ? new StringTemplate(value) : null;
+        }
+    }
+
+    private class FileAdapter extends SimpleJavaFileObject {
+        private final String filename;
+        private final Template template;
+
+        public FileAdapter(String filename, Template template) {
+            super(URI.create("myfo:/" + filename), Kind.SOURCE);
+            this.template = template;
+            this.filename = filename;
+        }
+
+        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+            return toString();
+        }
+
+        public String toString() {
+            return Template.Behavior.expandTemplate(template.expand(filename), defaultResolver);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/lib/combo/tools/javac/combo/Template.java	Mon Sep 09 17:11:55 2013 -0400
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2013, 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 tools.javac.combo;
+
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A template into which tags of the form {@code #\{KEY\}} or
+ * {@code #\{KEY.SUBKEY\}} can be expanded.
+ */
+public interface Template {
+    String expand(String selector);
+
+    interface Resolver {
+        public Template lookup(String key);
+    }
+
+    public static class Behavior {
+        /* Looks for expandable keys.  An expandable key can take the form:
+         *   #{MAJOR}
+         *   #{MAJOR.MINOR}
+         *   where MAJOR can be IDENTIFIER or IDENTIFIER[NUMERIC_INDEX]
+         *   and MINOR can be an identifier
+         */
+        private static final Pattern pattern = Pattern.compile("#\\{([A-Z_][A-Z0-9_]*(?:\\[\\d+\\])?)(?:\\.([A-Z_][A-Z0-9_]*))?\\}");
+
+        public static String expandTemplate(String template, final Map<String, Template> vars) {
+            return expandTemplate(template, new MapResolver(vars));
+        }
+
+        public static String expandTemplate(String template, Resolver res) {
+            CharSequence in = template;
+            StringBuffer out = new StringBuffer();
+            while (true) {
+                boolean more = false;
+                Matcher m = pattern.matcher(in);
+                while (m.find()) {
+                    String major = m.group(1);
+                    String minor = m.group(2);
+                    Template key = res.lookup(major);
+                    if (key == null)
+                        throw new IllegalStateException("Unknown major key " + major);
+
+                    String replacement = key.expand(minor == null ? "" : minor);
+                    more |= pattern.matcher(replacement).find();
+                    m.appendReplacement(out, replacement);
+                }
+                m.appendTail(out);
+                if (!more)
+                    return out.toString();
+                else {
+                    in = out;
+                    out = new StringBuffer();
+                }
+            }
+        }
+
+    }
+}
+
+class MapResolver implements Template.Resolver {
+    private final Map<String, Template> vars;
+
+    public MapResolver(Map<String, Template> vars) {this.vars = vars;}
+
+    public Template lookup(String key) {
+        return vars.get(key);
+    }
+}
+
+class ChainedResolver implements Template.Resolver {
+    private final Template.Resolver upstreamResolver, thisResolver;
+
+    public ChainedResolver(Template.Resolver upstreamResolver, Template.Resolver thisResolver) {
+        this.upstreamResolver = upstreamResolver;
+        this.thisResolver = thisResolver;
+    }
+
+    public Template.Resolver getUpstreamResolver() {
+        return upstreamResolver;
+    }
+
+    @Override
+    public Template lookup(String key) {
+        Template result = thisResolver.lookup(key);
+        if (result == null)
+            result = upstreamResolver.lookup(key);
+        return result;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/lib/combo/tools/javac/combo/TemplateTest.java	Mon Sep 09 17:11:55 2013 -0400
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2013, 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 tools.javac.combo;
+
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.testng.Assert.assertEquals;
+
+/**
+ * TemplateTest
+ */
+@Test
+public class TemplateTest {
+    Map<String, Template> vars = new HashMap<>();
+
+    @BeforeTest
+    void before() { vars.clear(); }
+
+    private void assertTemplate(String expected, String template) {
+        String result = Template.Behavior.expandTemplate(template, vars);
+        assertEquals(result, expected, "for " + template);
+    }
+
+    private String dotIf(String s) {
+        return s == null || s.isEmpty() ? "" : "." + s;
+    }
+
+    public void testTemplateExpansion() {
+        vars.put("A", s -> "a" + dotIf(s));
+        vars.put("B", s -> "b" + dotIf(s));
+        vars.put("C", s -> "#{A}#{B}");
+        vars.put("D", s -> "#{A" + dotIf(s) + "}#{B" + dotIf(s) + "}");
+        vars.put("_D", s -> "d");
+
+        assertTemplate("", "");
+        assertTemplate("foo", "foo");
+        assertTemplate("a", "#{A}");
+        assertTemplate("a", "#{A.}");
+        assertTemplate("a.FOO", "#{A.FOO}");
+        assertTemplate("aa", "#{A}#{A}");
+        assertTemplate("ab", "#{C}");
+        assertTemplate("ab", "#{C.FOO}");
+        assertTemplate("ab", "#{C.}");
+        assertTemplate("a.FOOb.FOO", "#{D.FOO}");
+        assertTemplate("ab", "#{D}");
+        assertTemplate("d", "#{_D}");
+        assertTemplate("#{A", "#{A");
+    }
+
+    public void testIndexedTemplate() {
+        vars.put("A[0]", s -> "a" );
+        vars.put("A[1]", s -> "b" );
+        vars.put("A[2]", s -> "c" );
+        vars.put("X", s -> "0");
+        assertTemplate("a", "#{A[0]}");
+        assertTemplate("b", "#{A[1]}");
+        assertTemplate("c", "#{A[2]}");
+    }
+
+    public void testAngleBrackets() {
+        vars.put("X", s -> "xyz");
+        assertTemplate("List<String> ls = xyz;", "List<String> ls = #{X};");
+    }
+
+    @Test(expectedExceptions = IllegalStateException.class )
+    public void testUnknownKey() {
+        assertTemplate("#{Q}", "#{Q}");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/bridge/template_tests/BridgeMethodTestCase.java	Mon Sep 09 17:11:55 2013 -0400
@@ -0,0 +1,421 @@
+/*
+ * Copyright (c) 2013, 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.
+ *
+ */
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringJoiner;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import tools.javac.combo.*;
+
+import static org.testng.Assert.fail;
+
+/**
+ * BridgeMethodTestCase -- used for asserting linkage to bridges under separate compilation.
+ *
+ * Example test case:
+ *     public void test1() throws IOException, ReflectiveOperationException {
+ *         compileSpec("C(Bc1(A))");
+ *         assertLinkage("C", LINKAGE_ERROR, "B1");
+ *         recompileSpec("C(Bc1(Ac0))", "A");
+ *          assertLinkage("C", "A0", "B1");
+ *     }
+ *
+ * This compiles A, B, and C, asserts that C.m()Object does not exist, asserts
+ * that C.m()Number eventually invokes B.m()Number, recompiles B, and then asserts
+ * that the result of calling C.m()Object now arrives at A.
+ *
+ * @author Brian Goetz
+ */
+
+@Test
+public abstract class BridgeMethodTestCase extends JavacTemplateTestBase {
+
+    private static final String TYPE_LETTERS = "ABCDIJK";
+
+    private static final String BASE_INDEX_CLASS = "class C0 {\n" +
+                                                   "    int val;\n" +
+                                                   "    C0(int val) { this.val = val; }\n" +
+                                                   "    public int getVal() { return val; }\n" +
+                                                   "}\n";
+    private static final String INDEX_CLASS_TEMPLATE = "class C#ID extends C#PREV {\n" +
+                                                       "    C#ID(int val) { super(val); }\n" +
+                                                       "}\n";
+
+
+
+    protected static String LINKAGE_ERROR = "-1";
+
+    private List<File> compileDirs = new ArrayList<>();
+
+    /**
+     * Compile all the classes in a class spec, and put them on the classpath.
+     *
+     * The spec is the specification for a nest of classes, using the following notation
+     * A, B represent abstract classes
+     * C represents a concrete class
+     * I, J, K represent interfaces
+     * Lowercase 'c' following a class means that the method m() is concrete
+     * Lowercase 'a' following a class or interface means that the method m() is abstract
+     * Lowercase 'd' following an interface means that the method m() is default
+     * A number 0, 1, or 2 following the lowercase letter indicates the return type of that method
+     * 0 = Object, 1 = Number, 2 = Integer (these form an inheritance chain so bridges are generated)
+     * A classes supertypes follow its method spec, in parentheses
+     * Examples:
+     *   C(Ia0, Jd0) -- C extends I and J, I has abstract m()Object, J has default m()Object
+     *   Cc1(Ia0) -- C has concrete m()Number, extends I with abstract m()Object
+     * If a type must appear multiple times, its full spec must be in the first occurrence
+     * Example:
+     *   C(I(Kd0), J(K))
+     */
+   protected void compileSpec(String spec) throws IOException {
+        compileSpec(spec, false);
+    }
+
+    /**
+     * Compile all the classes in a class spec, and assert that there were compilation errors.
+     */
+    protected void compileSpec(String spec, String... errorKeys) throws IOException {
+        compileSpec(spec, false, errorKeys);
+    }
+
+    protected void compileSpec(String spec, boolean debug, String... errorKeys) throws IOException {
+        ClassModel cm = new Parser(spec).parseClassModel();
+        for (int i = 0; i <= cm.maxIndex() ; i++) {
+            if (debug) System.out.println(indexClass(i));
+            addSourceFile(String.format("C%d.java", i), new StringTemplate(indexClass(i)));
+        }
+        for (Map.Entry<String, ClassModel> e : classes(cm).entrySet()) {
+            if (debug) System.out.println(e.getValue().toSource());
+            addSourceFile(e.getKey() + ".java", new StringTemplate(e.getValue().toSource()));
+        }
+        compileDirs.add(compile(true));
+        resetSourceFiles();
+        if (errorKeys.length == 0)
+            assertCompileSucceeded();
+        else
+            assertCompileErrors(errorKeys);
+    }
+
+    /**
+     * Recompile only a subset of classes in the class spec, as named by names,
+     * and put them on the classpath such that they shadow earlier versions of that class.
+     */
+    protected void recompileSpec(String spec, String... names) throws IOException {
+        List<String> nameList = Arrays.asList(names);
+        ClassModel cm = new Parser(spec).parseClassModel();
+        for (int i = 0; i <= cm.maxIndex() ; i++) {
+            addSourceFile(String.format("C%d.java", i), new StringTemplate(indexClass(i)));
+        }
+        for (Map.Entry<String, ClassModel> e : classes(cm).entrySet())
+            if (nameList.contains(e.getKey()))
+                addSourceFile(e.getKey() + ".java", new StringTemplate(e.getValue().toSource()));
+        compileDirs.add(compile(Arrays.asList(classPaths()), true));
+        resetSourceFiles();
+        assertCompileSucceeded();
+    }
+
+    protected void assertLinkage(String name, String... expected) throws ReflectiveOperationException {
+        for (int i=0; i<expected.length; i++) {
+            String e = expected[i];
+            if (e.equals(LINKAGE_ERROR)) {
+                try {
+                    int actual = invoke(name, i);
+                    fail("Expected linkage error, got" + fromNum(actual));
+                }
+                catch (LinkageError x) {
+                    // success
+                }
+            }
+            else {
+                if (e.length() == 1)
+                    e += "0";
+                int expectedInt = toNum(e);
+                int actual = invoke(name, i);
+                if (expectedInt != actual)
+                    fail(String.format("Expected %s but found %s for %s.m()%d", fromNum(expectedInt), fromNum(actual), name, i));
+            }
+        }
+    }
+
+    private Map<String, ClassModel> classes(ClassModel cm) {
+        HashMap<String, ClassModel> m = new HashMap<>();
+        classesHelper(cm, m);
+        return m;
+    }
+
+    private String indexClass(int index) {
+        if (index == 0) {
+            return BASE_INDEX_CLASS;
+        } else {
+            return INDEX_CLASS_TEMPLATE
+                    .replace("#ID", String.valueOf(index))
+                    .replace("#PREV", String.valueOf(index - 1));
+        }
+    }
+
+    private static String overrideName(int index) {
+        return "C" + index;
+    }
+
+    private void classesHelper(ClassModel cm, Map<String, ClassModel> m) {
+        if (!m.containsKey(cm.name))
+            m.put(cm.name, cm);
+        for (ClassModel s : cm.supertypes)
+            classesHelper(s, m);
+    }
+
+    private static String fromNum(int num) {
+        return String.format("%c%d", TYPE_LETTERS.charAt(num / 10), num % 10);
+    }
+
+    private static int toNum(String name, int index) {
+        return 10*(TYPE_LETTERS.indexOf(name.charAt(0))) + index;
+    }
+
+    private static int toNum(String string) {
+        return 10*(TYPE_LETTERS.indexOf(string.charAt(0))) + Integer.parseInt(string.substring(1, 2));
+    }
+
+    private int invoke(String name, int index) throws ReflectiveOperationException {
+        File[] files = classPaths();
+        Class clazz = loadClass(name, files);
+        Method[] ms = clazz.getMethods();
+        for (Method m : ms) {
+            if (m.getName().equals("m") && m.getReturnType().getName().equals(overrideName(index))) {
+                m.setAccessible(true);
+                Object instance = clazz.newInstance();
+                Object c0 = m.invoke(instance);
+                Method getVal = c0.getClass().getMethod("getVal");
+                getVal.setAccessible(true);
+                return (int)getVal.invoke(c0);
+            }
+        }
+        throw new NoSuchMethodError("cannot find method m()" + index + " in class " + name);
+    }
+
+    private File[] classPaths() {
+        File[] files = new File[compileDirs.size()];
+        for (int i=0; i<files.length; i++)
+            files[files.length - i - 1] = compileDirs.get(i);
+        return files;
+    }
+
+    @BeforeMethod
+    @Override
+    public void reset() {
+        compileDirs.clear();
+        super.reset();
+    }
+
+    private static class ClassModel {
+
+        enum MethodType {
+            ABSTRACT('a'), CONCRETE('c'), DEFAULT('d');
+
+            public final char designator;
+
+            MethodType(char designator) {
+                this.designator = designator;
+            }
+
+            public static MethodType find(char c) {
+                for (MethodType m : values())
+                    if (m.designator == c)
+                        return m;
+                throw new IllegalArgumentException();
+            }
+        }
+
+        private final String name;
+        private final boolean isInterface;
+        private final List<ClassModel> supertypes;
+        private final MethodType methodType;
+        private final int methodIndex;
+
+        private ClassModel(String name,
+                           boolean anInterface,
+                           List<ClassModel> supertypes,
+                           MethodType methodType,
+                           int methodIndex) {
+            this.name = name;
+            isInterface = anInterface;
+            this.supertypes = supertypes;
+            this.methodType = methodType;
+            this.methodIndex = methodIndex;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append(name);
+            if (methodType != null) {
+                sb.append(methodType.designator);
+                sb.append(methodIndex);
+            }
+            if (!supertypes.isEmpty()) {
+                sb.append("(");
+                for (int i=0; i<supertypes.size(); i++) {
+                    if (i > 0)
+                        sb.append(",");
+                    sb.append(supertypes.get(i).toString());
+                }
+                sb.append(")");
+            }
+            return sb.toString();
+        }
+
+        int maxIndex() {
+            int maxSoFar = methodIndex;
+            for (ClassModel cm : supertypes) {
+                maxSoFar = Math.max(cm.maxIndex(), maxSoFar);
+            }
+            return maxSoFar;
+        }
+
+        public String toSource() {
+            String extendsClause = "";
+            String implementsClause = "";
+            String methodBody = "";
+            boolean isAbstract = "AB".contains(name);
+
+            for (ClassModel s : supertypes) {
+                if (!s.isInterface) {
+                    extendsClause = String.format("extends %s", s.name);
+                    break;
+                }
+            }
+
+            StringJoiner sj = new StringJoiner(", ");
+            for (ClassModel s : supertypes)
+                if (s.isInterface)
+                    sj.add(s.name);
+            if (sj.length() > 0) {
+                if (isInterface)
+                    implementsClause = "extends " + sj.toString();
+                else
+                implementsClause = "implements " + sj.toString();
+            }
+            if (methodType != null) {
+                switch (methodType) {
+                    case ABSTRACT:
+                        methodBody = String.format("public abstract %s m();", overrideName(methodIndex));
+                        break;
+                    case CONCRETE:
+                        methodBody = String.format("public %s m() { return new %s(%d); };",
+                                overrideName(methodIndex), overrideName(methodIndex), toNum(name, methodIndex));
+                        break;
+                    case DEFAULT:
+                        methodBody = String.format("public default %s m() { return new %s(%d); };",
+                                overrideName(methodIndex), overrideName(methodIndex), toNum(name, methodIndex));
+                        break;
+
+                }
+            }
+
+            return String.format("public %s %s %s %s %s {  %s }", isAbstract ? "abstract" : "",
+                                 isInterface ? "interface" : "class",
+                                 name, extendsClause, implementsClause, methodBody);
+        }
+    }
+
+    private static class Parser {
+        private final String input;
+        private final char[] chars;
+        private int index;
+
+        private Parser(String s) {
+            input = s;
+            chars = s.toCharArray();
+        }
+
+        private char peek() {
+            return index < chars.length ? chars[index] : 0;
+        }
+
+        private boolean peek(String validChars) {
+            return validChars.indexOf(peek()) >= 0;
+        }
+
+        private char advanceIf(String validChars) {
+            if (peek(validChars))
+                return chars[index++];
+            else
+                return 0;
+        }
+
+        private char advanceIfDigit() {
+            return advanceIf("0123456789");
+        }
+
+        private int index() {
+            StringBuilder buf = new StringBuilder();
+            char c = advanceIfDigit();
+            while (c != 0) {
+                buf.append(c);
+                c = advanceIfDigit();
+            }
+            return Integer.valueOf(buf.toString());
+        }
+
+        private char advance() {
+            return chars[index++];
+        }
+
+        private char expect(String validChars) {
+            char c = advanceIf(validChars);
+            if (c == 0)
+                throw new IllegalArgumentException(String.format("Expecting %s at position %d of %s", validChars, index, input));
+            return c;
+        }
+
+        public ClassModel parseClassModel() {
+            List<ClassModel> supers = new ArrayList<>();
+            char name = expect(TYPE_LETTERS);
+            boolean isInterface = "IJK".indexOf(name) >= 0;
+            ClassModel.MethodType methodType = peek(isInterface ? "ad" : "ac") ? ClassModel.MethodType.find(advance()) : null;
+            int methodIndex = 0;
+            if (methodType != null) {
+                methodIndex = index();
+            }
+            if (peek() == '(') {
+                advance();
+                supers.add(parseClassModel());
+                while (peek() == ',') {
+                    advance();
+                    supers.add(parseClassModel());
+                }
+                expect(")");
+            }
+            return new ClassModel(new String(new char[]{ name }), isInterface, supers, methodType, methodIndex);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/bridge/template_tests/BridgeMethodsTemplateTest.java	Mon Sep 09 17:11:55 2013 -0400
@@ -0,0 +1,1082 @@
+/*
+ * Copyright (c) 2013, 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.
+ *
+ */
+import java.io.IOException;
+
+import org.testng.annotations.Test;
+
+/**
+ * BridgeMethodsTemplateTest
+ *
+ * @author Brian Goetz
+ */
+@Test
+public class BridgeMethodsTemplateTest extends BridgeMethodTestCase {
+
+    /*
+     * Cc1(A) -> Cc1(Ac0)
+     *
+     * 0*: Inherited from A
+     * 1: Declared in C
+     */
+    public void test1() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc1(A)");
+        assertLinkage("C", LINKAGE_ERROR, "C1");
+        recompileSpec("Cc1(Ac0)", "A");
+        assertLinkage("C", "A0", "C1");
+    }
+
+    /*
+     * Cc1(I) -> Cc1(Id0)
+     *
+     * 0*: Inherited default from I
+     * 1: Declared in C
+     */
+    public void test2() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc1(I)");
+        assertLinkage("C", LINKAGE_ERROR, "C1");
+        recompileSpec("Cc1(Id0)", "I");
+        assertLinkage("C", "I0", "C1");
+    }
+
+    /*
+     * C(Bc1(A)) -> C(Bc1(Ac0))
+     *
+     * 0*: Inherited from A
+     * 1: Inherited from B
+     */
+    public void test3() throws IOException, ReflectiveOperationException {
+        compileSpec("C(Bc1(A))");
+        assertLinkage("C", LINKAGE_ERROR, "B1");
+        recompileSpec("C(Bc1(Ac0))", "A");
+        assertLinkage("C", "A0", "B1");
+    }
+
+    /*
+     * C(B(Ac0)) -> C(Bc1(Ac0))
+     *
+     * 0: Inherited from B (through bridge)
+     * 1: Inherited from B
+     */
+    public void test4() throws IOException, ReflectiveOperationException {
+        compileSpec("C(B(Ac0))");
+        assertLinkage("C", "A0", LINKAGE_ERROR);
+        recompileSpec("C(Bc1(Ac0))", "B");
+        assertLinkage("C", "B1", "B1");
+    }
+
+    /*
+     * C(B(A)) -> C(Bc1(Ac0))
+     *
+     * 0: Inherited from B (through bridge)
+     * 1: Inherited from B
+     */
+    public void test5() throws IOException, ReflectiveOperationException {
+        compileSpec("C(B(A))");
+        assertLinkage("C", LINKAGE_ERROR, LINKAGE_ERROR);
+        recompileSpec("C(Bc1(Ac0))", "A", "B");
+        assertLinkage("C", "B1", "B1");
+    }
+
+    /*
+     * C(Ac1(I)) -> C(Ac1(Id0))
+     *
+     * 0*: Inherited default from I
+     * 1: Inherited from A
+     */
+    public void test6() throws IOException, ReflectiveOperationException {
+        compileSpec("C(Ac1(I))");
+        assertLinkage("C", LINKAGE_ERROR, "A1");
+        recompileSpec("C(Ac1(Id0))", "I");
+        assertLinkage("C", "I0", "A1");
+    }
+
+    /*
+     * C(A(Id0)) -> C(Ac1(Id0))
+     *
+     * 0: Inherited from A (through bridge)
+     * 1: Inherited from A
+     */
+    public void test7() throws IOException, ReflectiveOperationException {
+        compileSpec("C(A(Id0))");
+        assertLinkage("C", "I0", LINKAGE_ERROR);
+        recompileSpec("C(Ac1(Id0))", "A");
+        assertLinkage("C", "A1", "A1");
+    }
+
+    /*
+     * C(A(I)) -> C(Ac1(Id0))
+     *
+     * 0*: Inherited from A (through bridge)
+     * 1*: Inherited from A
+     */
+    public void test8() throws IOException, ReflectiveOperationException {
+        compileSpec("C(A(I))");
+        assertLinkage("C", LINKAGE_ERROR, LINKAGE_ERROR);
+        recompileSpec("C(Ac1(Id0))", "A", "I");
+        assertLinkage("C", "A1", "A1");
+    }
+
+    /*
+     * C(Id1(J)) -> C(Id1(Jd0))
+     *
+     * 0*: Inherited default from J
+     * 1: Inherited default from I
+     */
+    public void test9() throws IOException, ReflectiveOperationException {
+        compileSpec("C(Id1(J))");
+        assertLinkage("C", LINKAGE_ERROR, "I1");
+        recompileSpec("C(Id1(Jd0))", "J");
+        assertLinkage("C", "J0", "I1");
+    }
+
+    /*
+     * C(I(Jd0)) -> C(Id1(Jd0))
+     *
+     * 0: Inherited default from I (through bridge)
+     * 1: Inherited default from I
+     */
+    public void test10() throws IOException, ReflectiveOperationException {
+        compileSpec("C(I(Jd0))");
+        assertLinkage("C", "J0", LINKAGE_ERROR);
+        recompileSpec("C(Id1(Jd0))", "I");
+        assertLinkage("C", "I1", "I1");
+    }
+
+    /*
+     * C(I(J)) -> C(Id1(Jd0))
+     *
+     * 0: Inherited default from I (through bridge)
+     * 1: Inherited default from I
+     */
+    public void test11() throws IOException, ReflectiveOperationException {
+        compileSpec("C(I(J))");
+        assertLinkage("C", LINKAGE_ERROR, LINKAGE_ERROR);
+        recompileSpec("C(Id1(Jd0))", "I", "J");
+        assertLinkage("C", "I1", "I1");
+    }
+
+    /*
+     * Cc2(B(Ac0)) -> Cc2(Bc1(Ac0))
+     *
+     * 0: Declared in C (through bridge)
+     * 1*: Inherited from B
+     * 2: Declared in C
+     */
+    public void test12() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(B(Ac0))");
+        assertLinkage("C", "C2", LINKAGE_ERROR, "C2");
+        recompileSpec("Cc2(Bc1(Ac0))", "B");
+        assertLinkage("C", "C2", "B1", "C2");
+    }
+
+    /*
+     * Cc2(B(Aa0)) -> Cc2(Bc1(Aa0))
+     *
+     * 0: Bridge in C (through bridge)
+     * 1*: Inherited from B
+     * 2: Declared in C
+     */
+    public void test13() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(B(Aa0))");
+        assertLinkage("C", "C2", LINKAGE_ERROR, "C2");
+        recompileSpec("Cc2(Bc1(Aa0))", "B");
+        assertLinkage("C", "C2", "B1", "C2");
+    }
+
+    /*
+     * Cc2(Bc1(A)) -> Cc2(Bc1(Ac0))
+     *
+     * 0*: Inherited from A
+     * 1: Declared in C (through bridge)
+     * 2: Declared in C
+     */
+    public void test14() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(Bc1(A))");
+        assertLinkage("C", LINKAGE_ERROR, "C2", "C2");
+        recompileSpec("Cc2(Bc1(Ac0))", "A");
+        assertLinkage("C", "A0", "C2", "C2");
+    }
+
+    /*
+     * Cc2(Ba1(A)) -> Cc2(Ba1(Ac0))
+     *
+     * 0*: Inherited from A
+     * 1: Declared in C (through bridge)
+     * 2: Declared in C
+     */
+    public void test15() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(Ba1(A))");
+        assertLinkage("C", LINKAGE_ERROR, "C2", "C2");
+        recompileSpec("Cc2(Ba1(Ac0))", "A");
+        assertLinkage("C", "A0", "C2", "C2");
+    }
+
+    /*
+     * Cc2(B(A)) -> Cc2(Bc1(Ac0))
+     *
+     * 0*: Inherited from B (through bridge)
+     * 1*: Inherited from B
+     * 2: Declared in C
+     */
+    public void test16() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(B(A))");
+        assertLinkage("C", LINKAGE_ERROR, LINKAGE_ERROR, "C2");
+        recompileSpec("Cc2(Bc1(Ac0))", "B", "A");
+        assertLinkage("C", "B1", "B1", "C2");
+    }
+
+    /*
+     * Cc2(A(Id0)) -> Cc2(Ac1(Id0))
+     *
+     * 0: Declared in C (through bridge)
+     * 1*: Inherited from A
+     * 2: Declared in C
+     */
+    public void test17() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(A(Id0))");
+        assertLinkage("C", "C2", LINKAGE_ERROR, "C2");
+        recompileSpec("Cc2(Ac1(Id0))", "A");
+        assertLinkage("C", "C2", "A1", "C2");
+    }
+
+    /*
+     * Cc2(A(Ia0)) -> Cc2(Ac1(Ia0))
+     *
+     * 0: Declared in C (through bridge)
+     * 1*: Inherited from A
+     * 2: Declared in C
+     */
+    public void test18() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(A(Ia0))");
+        assertLinkage("C", "C2", LINKAGE_ERROR, "C2");
+        recompileSpec("Cc2(Ac1(Ia0))", "A");
+        assertLinkage("C", "C2", "A1", "C2");
+    }
+
+    /*
+     * Cc2(Ac1(I)) -> Cc2(Ac1(Id0))
+     *
+     * 0*: Inherited from I
+     * 1: Declared in C (through bridge)
+     * 2: Declared in C
+     */
+    public void test19() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(Ac1(I))");
+        assertLinkage("C", LINKAGE_ERROR, "C2", "C2");
+        recompileSpec("Cc2(Ac1(Id0))", "I");
+        assertLinkage("C", "I0", "C2", "C2");
+    }
+
+    /*
+     * Cc2(Aa1(I)) -> Cc2(Aa1(Id0))
+     *
+     * 0*: Inherited from I
+     * 1: Declared in C (through bridge)
+     * 2: Declared in C
+     */
+    public void test20() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(Aa1(I))");
+        assertLinkage("C", LINKAGE_ERROR, "C2", "C2");
+        recompileSpec("Cc2(Aa1(Id0))", "I");
+        assertLinkage("C", "I0", "C2", "C2");
+    }
+
+    /*
+     * Cc2(A(I)) -> Cc2(Ac1(Id0))
+     *
+     * 0*: Inherited from A (through bridge)
+     * 1*: Inherited from A
+     * 2: Declared in C
+     */
+    public void test21() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(A(I))");
+        assertLinkage("C", LINKAGE_ERROR, LINKAGE_ERROR, "C2");
+        recompileSpec("Cc2(Ac1(Id0))", "A", "I");
+        assertLinkage("C", "A1", "A1", "C2");
+    }
+
+    /*
+     * Cc2(J(Id0)) -> Cc2(Jd1(Id0))
+     *
+     * 0: Declared in C (through bridge)
+     * 1*: Inherited default from J
+     * 2: Declared in C
+     */
+    public void test22() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(J(Id0))");
+        assertLinkage("C", "C2", LINKAGE_ERROR, "C2");
+        recompileSpec("Cc2(Jd1(Id0))", "J");
+        assertLinkage("C", "C2", "J1", "C2");
+    }
+
+    /*
+     * Cc2(J(Ia0)) -> Cc2(Jd1(Ia0))
+     *
+     * 0: Declared in C (through bridge)
+     * 1*: Inherited default from J
+     * 2: Declared in C
+     */
+    public void test23() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(J(Ia0))");
+        assertLinkage("C", "C2", LINKAGE_ERROR, "C2");
+        recompileSpec("Cc2(Jd1(Ia0))", "J");
+        assertLinkage("C", "C2", "J1", "C2");
+    }
+
+    /*
+     * Cc2(Jd1(I)) -> Cc2(Jd1(Id0))
+     *
+     * 0*: Inherited default from I
+     * 1: Declared in C (through bridge)
+     * 2: Declared in C
+     */
+    public void test24() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(Jd1(I))");
+        assertLinkage("C", LINKAGE_ERROR, "C2", "C2");
+        recompileSpec("Cc2(Jd1(Id0))", "I");
+        assertLinkage("C", "I0", "C2", "C2");
+    }
+
+    /*
+     * Cc2(Ja1(I)) -> Cc2(Ja1(Id0))
+     *
+     * 0*: Inherited default from I
+     * 1: Declared in C (through bridge)
+     * 2: Declared in C
+     */
+    public void test25() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(Ja1(I))");
+        assertLinkage("C", LINKAGE_ERROR, "C2", "C2");
+        recompileSpec("Cc2(Ja1(Id0))", "I");
+        assertLinkage("C", "I0", "C2", "C2");
+    }
+
+    /*
+     * Cc2(J(I)) -> Cc2(Jd1(Id0))
+     *
+     * 0*: Inherited default from J (through bridge)
+     * 1*: Inherited default from J
+     * 2: Declared in C
+     */
+    public void test26() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(J(I))");
+        assertLinkage("C", LINKAGE_ERROR, LINKAGE_ERROR, "C2");
+        recompileSpec("Cc2(Jd1(Id0))", "J", "I");
+        assertLinkage("C", "J1", "J1", "C2");
+    }
+
+    /*
+     * C(Ac1, I) -> C(Ac1, Id0)
+     *
+     * 0*: Inherited default from I
+     * 1: Inherited from A
+     */
+    public void test27() throws IOException, ReflectiveOperationException {
+        compileSpec("C(Ac1,I)");
+        assertLinkage("C", LINKAGE_ERROR, "A1");
+        recompileSpec("C(Ac1,Id0)", "I");
+        assertLinkage("C", "I0", "A1");
+    }
+
+    /*
+     * C(A, Id0) -> C(Ac1, Id0)
+     *
+     * 0*: Inherited default from I
+     * 1: Inherited from A
+     */
+    public void test28() throws IOException, ReflectiveOperationException {
+        compileSpec("C(A,Id0)");
+        assertLinkage("C", "I0", LINKAGE_ERROR);
+        recompileSpec("C(Ac1,Id0)", "A");
+        assertLinkage("C", "I0", "A1");
+    }
+
+    /*
+     * C(A, I) -> C(Ac1, Id0)
+     *
+     * 0*: Inherited default from I
+     * 1: Inherited from A
+     */
+    public void test29() throws IOException, ReflectiveOperationException {
+        compileSpec("C(A,I)");
+        assertLinkage("C", LINKAGE_ERROR, LINKAGE_ERROR);
+        recompileSpec("C(Ac1,Id0)", "A", "I");
+        assertLinkage("C", "I0", "A1");
+    }
+
+    /*
+     * Cc2(Ac1, I) -> Cc2(Ac1, Id0)
+     *
+     * 0*: Inherited default from I
+     * 1: Declared in C (through bridge)
+     * 2: Declared in C
+     */
+    public void test30() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(Ac1,I)");
+        assertLinkage("C", LINKAGE_ERROR, "C2", "C2");
+        recompileSpec("Cc2(Ac1,Id0)", "I");
+        assertLinkage("C", "I0", "C2", "C2");
+    }
+
+    /*
+     * Cc2(Aa1, I) -> Cc2(Aa1, Id0)
+     *
+     * 0*: Inherited default from I
+     * 1: Declared in C (through bridge)
+     * 2: Declared in C
+     */
+    public void test31() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(Aa1,I)");
+        assertLinkage("C", LINKAGE_ERROR, "C2", "C2");
+        recompileSpec("Cc2(Aa1,Id0)", "I");
+        assertLinkage("C", "I0", "C2", "C2");
+    }
+
+    /*
+     * Cc2(A, Id0) -> Cc2(Ac1, Id0)
+     *
+     * 0: Declared in C (through bridge)
+     * 1*: Inherited from A
+     * 2: Declared in C
+     */
+    public void test32() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(A,Id0)");
+        assertLinkage("C", "C2", LINKAGE_ERROR, "C2");
+        recompileSpec("Cc2(Ac1,Id0)", "A");
+        assertLinkage("C", "C2", "A1", "C2");
+    }
+
+    /*
+     * Cc2(A, Ia0) -> Cc2(Ac1, Ia0)
+     *
+     * 0: Declared in C (through bridge)
+     * 1*: Inherited from A
+     * 2: Declared in C
+     */
+    public void test33() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(A,Ia0)");
+        assertLinkage("C", "C2", LINKAGE_ERROR, "C2");
+        recompileSpec("Cc2(Ac1,Ia0)", "A");
+        assertLinkage("C", "C2", "A1", "C2");
+    }
+
+    /*
+     * Cc2(A, I) -> Cc2(Ac1, Id0)
+     *
+     * 0*: Inherited from A
+     * 1*: Inherited default from I
+     * 2: Declared in C
+     */
+    public void test34() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(A,I)");
+        assertLinkage("C", LINKAGE_ERROR, LINKAGE_ERROR, "C2");
+        recompileSpec("Cc2(Ac1,Id0)", "A", "I");
+        assertLinkage("C", "I0", "A1", "C2");
+    }
+
+    /*
+     * Cc2(Id0, J) -> Cc2(Id0, Jd1)
+     *
+     * 0: Declared in C (through bridge)
+     * 1*: Inherited default from J
+     * 2: Declared in C
+     */
+    public void test35() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(Id0,J)");
+        assertLinkage("C", "C2", LINKAGE_ERROR, "C2");
+        recompileSpec("Cc2(Id0,Jd1)", "J");
+        assertLinkage("C", "C2", "J1", "C2");
+    }
+
+    /*
+     * Cc2(Ia0, J) -> Cc2(Ia0, Jd1)
+     *
+     * 0: Declared in C (through bridge)
+     * 1*: Inherited default from J
+     * 2: Declared in C
+     */
+    public void test36() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(Ia0,J)");
+        assertLinkage("C", "C2", LINKAGE_ERROR, "C2");
+        recompileSpec("Cc2(Ia0,Jd1)", "J");
+        assertLinkage("C", "C2", "J1", "C2");
+    }
+
+    /*
+     * Cc2(I, J) -> Cc2(Id0, Jd1)
+     *
+     * 0*: Inherited default from I
+     * 1*: Inherited default from J
+     * 2: Declared in C
+     */
+    public void test37() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(I,J)");
+        assertLinkage("C", LINKAGE_ERROR, LINKAGE_ERROR, "C2");
+        recompileSpec("Cc2(Id0,Jd1)", "I", "J");
+        assertLinkage("C", "I0", "J1", "C2");
+    }
+
+    /*
+     * C(A(Id0), J(Id0)) -> C(Ac1(Id0), J(Id0))
+     *
+     * 0: Inherited default from I
+     * 0*: Inherited from A (through bridge)
+     * 1*: Inherited from A
+     */
+    public void test38() throws IOException, ReflectiveOperationException {
+        compileSpec("C(A(Id0),J(Id0))");
+        assertLinkage("C", "I0", LINKAGE_ERROR);
+        recompileSpec("C(Ac1(Id0),J(Id0))", "A");
+        assertLinkage("C", "A1", "A1");
+    }
+
+    /*
+     * C(A(Id0), J(Id0)) -> C(A(Id0), Jd1(Id0))
+     *
+     * 0: Inherited default from I
+     * 0: Inherited default from J (through bridge)
+     * 1*: Inherited default from J
+     */
+    public void test39() throws IOException, ReflectiveOperationException {
+        compileSpec("C(A(Id0),J(Id0))");
+        assertLinkage("C", "I0", LINKAGE_ERROR);
+        recompileSpec("C(A(Id0),Jd1(Id0))", "J");
+        assertLinkage("C", "J1", "J1");
+    }
+
+    /*
+     * C(A(Id0), J(Id0)) -> C(Ac2(Id0), Jd1(Id0))
+     *
+     * 0: Inherited default from I
+     * 0*: Inherited from A (new bridge in A beats new bridge in J)
+     * 1*: Inherited default from J
+     * 2: Inherited from A
+     */
+    public void test40() throws IOException, ReflectiveOperationException {
+        compileSpec("C(A(Id0),J(Id0))");
+        assertLinkage("C", "I0", LINKAGE_ERROR);
+        recompileSpec("C(Ac2(Id0),Jd1(Id0))", "A", "J");
+        assertLinkage("C", "A2", "J1", "A2");
+    }
+
+    /*
+     * C(J(Id0), K(Id0)) -> C(Jd1(Id0), K(Id0))
+     *
+     * 0: Inherited from I
+     * 0*: Inherited default from J (through bridge)
+     * 1: Inherited default from J
+     */
+    public void test41() throws IOException, ReflectiveOperationException {
+        compileSpec("C(J(Id0),K(Id0))");
+        assertLinkage("C", "I0", LINKAGE_ERROR);
+        recompileSpec("C(Jd1(Id0),K(Id0))", "J");
+        assertLinkage("C", "J1", "J1");
+    }
+
+    /*
+     * C(Ac2(Id0), J(Id0)) -> C(Ac2(Id0), Jd1(Id0))
+     *
+     * 0: Inherited from A (bridge in A beats new bridge in J)
+     * 1*: Inherited default from J
+     * 2: Inherited from A
+     */
+    public void test42() throws IOException, ReflectiveOperationException {
+        compileSpec("C(Ac2(Id0),J(Id0))");
+        assertLinkage("C", "A2", LINKAGE_ERROR, "A2");
+        recompileSpec("C(Ac2(Id0),Jd1(Id0))", "J");
+        assertLinkage("C", "A2", "J1", "A2");
+    }
+
+    /*
+     * C(Ac2(Ia0), J(Ia0)) -> C(Ac2(Ia0), Jd1(Ia0))
+     *
+     * 0: Inherited from A (bridge in A beats new bridge in J)
+     * 1*: Inherited default from J
+     * 2: Inherited from A
+     */
+    public void test43() throws IOException, ReflectiveOperationException {
+        compileSpec("C(Ac2(Ia0),J(Ia0))");
+        assertLinkage("C", "A2", LINKAGE_ERROR, "A2");
+        recompileSpec("C(Ac2(Ia0),Jd1(Ia0))", "J");
+        assertLinkage("C", "A2", "J1", "A2");
+    }
+
+    /*
+     * C(A(Id0), Jd1(Id0)) -> C(Ac2(Id0), Jd1(Id0))
+     *
+     * 0: Inherited from J
+     * 0*: Inherited from A (new bridge in A beats bridge in J)
+     * 1: Inherited default from J
+     * 2*: Inherited from A
+     */
+    public void test44() throws IOException, ReflectiveOperationException {
+        compileSpec("C(A(Id0),Jd1(Id0))");
+        assertLinkage("C", "J1", "J1", LINKAGE_ERROR);
+        recompileSpec("C(Ac2(Id0),Jd1(Id0))", "A");
+        assertLinkage("C", "A2", "J1", "A2");
+    }
+
+    /*
+     * C(A(Ia0), Jd1(Ia0)) -> C(Ac2(Ia0), Jd1(Ia0))
+     *
+     * 0: Inherited from J
+     * 0*: Inherited from A (new bridge in A beats bridge in J)
+     * 1: Inherited default from J
+     * 2*: Inherited from A
+     */
+    public void test45() throws IOException, ReflectiveOperationException {
+        compileSpec("C(A(Ia0),Jd1(Ia0))");
+        assertLinkage("C", "J1", "J1", LINKAGE_ERROR);
+        recompileSpec("C(Ac2(Ia0),Jd1(Ia0))", "A");
+        assertLinkage("C", "A2", "J1", "A2");
+    }
+
+    /*
+     * Cc2(A(Id0), J(Id0)) -> Cc2(Ac1(Id0), J(Id0))
+     *
+     * 0: Declared in C (through bridge)
+     * 1*: Inherited from A
+     * 2: Declared in C
+     */
+    public void test46() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(A(Id0),J(Id0))");
+        assertLinkage("C", "C2", LINKAGE_ERROR, "C2");
+        recompileSpec("Cc2(Ac1(Id0),J(Id0))", "A");
+        assertLinkage("C", "C2", "A1", "C2");
+    }
+
+    /*
+     * Cc2(A(Ia0), J(Ia0)) -> Cc2(Ac1(Ia0), J(Ia0))
+     *
+     * 0: Declared in C (through bridge)
+     * 1*: Inherited from A
+     * 2: Declared in C
+     */
+    public void test47() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(A(Ia0),J(Ia0))");
+        assertLinkage("C", "C2", LINKAGE_ERROR, "C2");
+        recompileSpec("Cc2(Ac1(Ia0),J(Ia0))", "A");
+        assertLinkage("C", "C2", "A1", "C2");
+    }
+
+    /*
+     * Cc2(A(Id0), J(Id0)) -> Cc2(A(Id0), Jd1(Id0))
+     *
+     * 0: Declared in C (through bridge)
+     * 1*: Inherited default from J
+     * 2: Declared in C
+     */
+    public void test48() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(A(Id0),J(Id0))");
+        assertLinkage("C", "C2", LINKAGE_ERROR, "C2");
+        recompileSpec("Cc2(A(Id0),Jd1(Id0))", "J");
+        assertLinkage("C", "C2", "J1", "C2");
+    }
+
+    /*
+     * Cc2(A(Ia0), J(Ia0)) -> Cc2(A(Ia0), Jd1(Ia0))
+     *
+     * 0: Declared in C (through bridge)
+     * 1*: Inherited default from J
+     * 2: Declared in C
+     */
+    public void test49() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(A(Ia0),J(Ia0))");
+        assertLinkage("C", "C2", LINKAGE_ERROR, "C2");
+        recompileSpec("Cc2(A(Ia0),Jd1(Ia0))", "J");
+        assertLinkage("C", "C2", "J1", "C2");
+    }
+
+
+    /*
+     * Cc3(A(Id0), J(Id0)) -> Cc3(Ac1(Id0), Jd2(Id0))
+     *
+     * 0: Bridge in C
+     * 1*: Inherited from A
+     * 2*: Inherited default from J
+     * 3: Declared in C
+     */
+    public void test50() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc3(A(Id0),J(Id0))");
+        assertLinkage("C", "C3", LINKAGE_ERROR, LINKAGE_ERROR, "C3");
+        recompileSpec("Cc3(Ac1(Id0),Jd2(Id0))", "A", "J");
+        assertLinkage("C", "C3", "A1", "J2", "C3");
+    }
+
+    /*
+     * Cc3(A(Ia0), J(Ia0)) -> Cc3(Ac1(Ia0), Jd2(Ia0))
+     *
+     * 0: Bridge in C
+     * 1*: Inherited from A
+     * 2*: Inherited default from J
+     * 3: Declared in C
+     */
+    public void test51() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc3(A(Ia0),J(Ia0))");
+        assertLinkage("C", "C3", LINKAGE_ERROR, LINKAGE_ERROR, "C3");
+        recompileSpec("Cc3(Ac1(Ia0),Jd2(Ia0))", "A", "J");
+        assertLinkage("C", "C3", "A1", "J2", "C3");
+    }
+
+    /*
+     * Cc2(J(Id0), K(Id0)) -> Cc2(Jd1(Id0), K(Id0))
+     *
+     * 0: Declared in C (through bridge)
+     * 1*: Inherited default from J
+     * 2: Declared in C
+     */
+    public void test52() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(J(Id0),K(Id0))");
+        assertLinkage("C", "C2", LINKAGE_ERROR, "C2");
+        recompileSpec("Cc2(Jd1(Id0),K(Id0))", "J");
+        assertLinkage("C", "C2", "J1", "C2");
+    }
+
+    /*
+     * Cc2(J(Ia0), K(Ia0)) -> Cc2(Jd1(Ia0), K(Ia0))
+     *
+     * 0: Declared in C (through bridge)
+     * 1*: Inherited default from J
+     * 2: Declared in C
+     */
+    public void test53() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc2(J(Ia0),K(Ia0))");
+        assertLinkage("C", "C2", LINKAGE_ERROR, "C2");
+        recompileSpec("Cc2(Jd1(Ia0),K(Ia0))", "J");
+        assertLinkage("C", "C2", "J1", "C2");
+    }
+
+    /*
+     * Cc3(J(Id0), K(Id0)) -> Cc3(Jd1(Id0), Kd2(Id0))
+     *
+     * 0: Declared in C (through bridge)
+     * 1*: Inherited default from J
+     * 2*: Inherited default from K
+     * 3: Declared in C
+     */
+    public void test54() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc3(J(Id0),K(Id0))");
+        assertLinkage("C", "C3", LINKAGE_ERROR, LINKAGE_ERROR, "C3");
+        recompileSpec("Cc3(Jd1(Id0),Kd2(Id0))", "J", "K");
+        assertLinkage("C", "C3", "J1", "K2", "C3");
+    }
+
+    /*
+     * Cc3(J(Ia0), K(Ia0)) -> Cc3(Jd1(Ia0), Kd2(Ia0))
+     *
+     * 0: Declared in C (through bridge)
+     * 1*: Inherited default from J
+     * 2*: Inherited default from K
+     * 3: Declared in C
+     */
+    public void test55() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc3(J(Ia0),K(Ia0))");
+        assertLinkage("C", "C3", LINKAGE_ERROR, LINKAGE_ERROR, "C3");
+        recompileSpec("Cc3(Jd1(Ia0),Kd2(Ia0))", "J", "K");
+        assertLinkage("C", "C3", "J1", "K2", "C3");
+    }
+
+    /*
+     * Cc3(Ac1(Id0), J(Id0)) -> Cc3(Ac1(Id0), Jd2(Id0))
+     *
+     * 0: Declared in C (through bridge)
+     * 1: Declared in C (through bridge)
+     * 2*: Inherited default from J
+     * 3: Declared in C
+     */
+    public void test56() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc3(Ac1(Id0),J(Id0))");
+        assertLinkage("C", "C3", "C3", LINKAGE_ERROR, "C3");
+        recompileSpec("Cc3(Ac1(Id0),Jd2(Id0))", "J");
+        assertLinkage("C", "C3", "C3", "J2", "C3");
+    }
+
+    /*
+     * Cc3(Ac1(Ia0), J(Ia0)) -> Cc3(Ac1(Ia0), Jd2(Ia0))
+     *
+     * 0: Declared in C (through bridge)
+     * 1: Declared in C (through bridge)
+     * 2*: Inherited default from J
+     * 3: Declared in C
+     */
+    public void test57() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc3(Ac1(Ia0),J(Ia0))");
+        assertLinkage("C", "C3", "C3", LINKAGE_ERROR, "C3");
+        recompileSpec("Cc3(Ac1(Ia0),Jd2(Ia0))", "J");
+        assertLinkage("C", "C3", "C3", "J2", "C3");
+    }
+
+    /*
+     * Cc3(Aa1(Id0), J(Id0)) -> Cc3(Aa1(Id0), Jd2(Id0))
+     *
+     * 0: Declared in C (through bridge)
+     * 1: Declared in C (through bridge)
+     * 2*: Inherited default from J
+     * 3: Declared in C
+     */
+    public void test58() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc3(Aa1(Id0),J(Id0))");
+        assertLinkage("C", "C3", "C3", LINKAGE_ERROR, "C3");
+        recompileSpec("Cc3(Aa1(Id0),Jd2(Id0))", "J");
+        assertLinkage("C", "C3", "C3", "J2", "C3");
+    }
+
+    /*
+     * Cc3(Aa1(Ia0), J(Ia0)) -> Cc3(Aa1(Ia0), Jd2(Ia0))
+     *
+     * 0: Declared in C (through bridge)
+     * 1: Declared in C (through bridge)
+     * 2*: Inherited default from J
+     * 3: Declared in C
+     */
+    public void test59() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc3(Aa1(Ia0),J(Ia0))");
+        assertLinkage("C", "C3", "C3", LINKAGE_ERROR, "C3");
+        recompileSpec("Cc3(Aa1(Ia0),Jd2(Ia0))", "J");
+        assertLinkage("C", "C3", "C3", "J2", "C3");
+    }
+
+    /*
+     * Cc3(A(Id0), Jd2(Id0)) -> Cc3(Ac1(Id0), Jd2(Id0))
+     *
+     * 0: Declared in C (through bridge)
+     * 1*: Inherited from A
+     * 2: Declared in C (through bridge)
+     * 3: Declared in C
+     */
+    public void test60() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc3(A(Id0),Jd2(Id0))");
+        assertLinkage("C", "C3", LINKAGE_ERROR, "C3", "C3");
+        recompileSpec("Cc3(Ac1(Id0),Jd2(Id0))", "A");
+        assertLinkage("C", "C3", "A1", "C3", "C3");
+    }
+
+    /*
+     * Cc3(A(Im0), Jd2(Ia0)) -> Cc3(Ac1(Im0), Jd2(Ia0))
+     *
+     * 0: Declared in C (through bridge)
+     * 1*: Inherited from A
+     * 2: Declared in C (through bridge)
+     * 3: Declared in C
+     */
+    public void test61() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc3(A(Ia0),Jd2(Ia0))");
+        assertLinkage("C", "C3", LINKAGE_ERROR, "C3", "C3");
+        recompileSpec("Cc3(Ac1(Ia0),Jd2(Ia0))", "A");
+        assertLinkage("C", "C3", "A1", "C3", "C3");
+    }
+
+    /*
+     * Cc3(A(Im0), Ja2(Id0)) -> Cc3(Ac1(Id0), Ja2(Id0))
+     *
+     * 0: Declared in C (through bridge)
+     * 1*: Inherited from A
+     * 2: Declared in C (through bridge)
+     * 3: Declared in C
+     */
+    public void test62() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc3(A(Id0),Ja2(Id0))");
+        assertLinkage("C", "C3", LINKAGE_ERROR, "C3", "C3");
+        recompileSpec("Cc3(Ac1(Id0),Ja2(Id0))", "A");
+        assertLinkage("C", "C3", "A1", "C3", "C3");
+    }
+
+    /*
+     * Cc3(A(Im0), Ja2(Ia0)) -> Cc3(Ac1(Ia0), Ja2(Ia0))
+     *
+     * 0: Declared in C (through bridge)
+     * 1*: Inherited from A
+     * 2: Declared in C (through bridge)
+     * 3: Declared in C
+     */
+    public void test63() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc3(A(Ia0),Ja2(Ia0))");
+        assertLinkage("C", "C3", LINKAGE_ERROR, "C3", "C3");
+        recompileSpec("Cc3(Ac1(Ia0),Ja2(Ia0))", "A");
+        assertLinkage("C", "C3", "A1", "C3", "C3");
+    }
+
+    /*
+     * Cc3(Jd1(Id0), K(Id0)) -> Cc3(Jd1(Id0), Kd2(Id0))
+     *
+     * 0: Declared in C (through bridge)
+     * 1: Declared in C (through bridge)
+     * 2*: Inherited default from K
+     * 3: Declared in C
+     */
+    public void test64() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc3(Jd1(Id0),K(Id0))");
+        assertLinkage("C", "C3", "C3", LINKAGE_ERROR, "C3");
+        recompileSpec("Cc3(Jd1(Id0),Kd2(Id0))", "K");
+        assertLinkage("C", "C3", "C3", "K2", "C3");
+    }
+
+    /*
+     * Cc3(Jd1(Ia0), K(Ia0)) -> Cc3(Jd1(Ia0), Kd2(Ia0))
+     *
+     * 0: Declared in C (through bridge)
+     * 1: Declared in C (through bridge)
+     * 2*: Inherited default from K
+     * 3: Declared in C
+     */
+    public void test65() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc3(Jd1(Ia0),K(Ia0))");
+        assertLinkage("C", "C3", "C3", LINKAGE_ERROR, "C3");
+        recompileSpec("Cc3(Jd1(Ia0),Kd2(Ia0))", "K");
+        assertLinkage("C", "C3", "C3", "K2", "C3");
+    }
+
+    /*
+     * Cc3(Ja1(Id0), K(Id0)) -> Cc3(Ja1(Id0), Kd2(Id0))
+     *
+     * 0: Declared in C (through bridge)
+     * 1: Declared in C (through bridge)
+     * 2*: Inherited default from K
+     * 3: Declared in C
+     */
+    public void test66() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc3(Jd1(Id0),K(Id0))");
+        assertLinkage("C", "C3", "C3", LINKAGE_ERROR, "C3");
+        recompileSpec("Cc3(Jd1(Id0),Kd2(Id0))", "K");
+        assertLinkage("C", "C3", "C3", "K2", "C3");
+    }
+
+    /*
+     * Cc3(Ja1(Ia0), K(Ia0)) -> Cc3(Ja1(Ia0), Kd2(Ia0))
+     *
+     * 0: Declared in C (through bridge)
+     * 1: Declared in C (through bridge)
+     * 2*: Inherited default from K
+     * 3: Declared in C
+     */
+    public void test67() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc3(Jd1(Ia0),K(Ia0))");
+        assertLinkage("C", "C3", "C3", LINKAGE_ERROR, "C3");
+        recompileSpec("Cc3(Jd1(Ia0),Kd2(Ia0))", "K");
+        assertLinkage("C", "C3", "C3", "K2", "C3");
+    }
+
+    // Dan's set A
+    public void testA1() throws IOException, ReflectiveOperationException {
+        compileSpec("C(Id0)");
+        assertLinkage("C", "I0");
+    }
+
+    public void testA2() throws IOException, ReflectiveOperationException {
+        compileSpec("C(A(Id0))");
+        assertLinkage("C", "I0");
+    }
+
+    public void testA3() throws IOException, ReflectiveOperationException {
+        compileSpec("C(A(Id0),J)");
+        assertLinkage("C", "I0");
+    }
+
+    public void testA4() throws IOException, ReflectiveOperationException {
+        compileSpec("D(C(Id0),Jd0(Id0))");
+        assertLinkage("D", "J0");
+        assertLinkage("C", "I0");
+    }
+
+    public void testA5() throws IOException, ReflectiveOperationException {
+        compileSpec("C(A(Id0),Jd0)",
+                    "compiler.err.types.incompatible.unrelated.defaults");
+    }
+
+    public void testA6() throws IOException, ReflectiveOperationException {
+        compileSpec("C(A(Ia0,Jd0))",
+                    "compiler.err.does.not.override.abstract",
+                    "compiler.err.types.incompatible.abstract.default");
+    }
+
+    public void testA7() throws IOException, ReflectiveOperationException {
+        compileSpec("C(A(Id0,Jd0))",
+                    "compiler.err.types.incompatible.unrelated.defaults");
+    }
+
+    public void testA8() throws IOException, ReflectiveOperationException {
+        compileSpec("C(A(Ia0),J)", "compiler.err.does.not.override.abstract");
+    }
+
+    public void testA9() throws IOException, ReflectiveOperationException {
+        compileSpec("C(Ac0(Id0))");
+        assertLinkage("C", "A0");
+    }
+
+    public void testA10() throws IOException, ReflectiveOperationException {
+        compileSpec("C(Aa0,I)", "compiler.err.does.not.override.abstract");
+    }
+
+    public void testA11() throws IOException, ReflectiveOperationException {
+        compileSpec("C(Ac0,Id0)");
+        assertLinkage("C", "A0");
+    }
+
+    // Dan's set B
+
+    /* B1 can't be done, needs a second concrete class D
+    public void testB1() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc1(Dc0)");
+        assertLinkage("C", "C1", "C1");
+        assertLinkage("D", "A0", LINKAGE_ERROR);
+    }
+    */
+
+    public void testB2() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc1(Ac0)");
+        assertLinkage("C", "C1", "C1");
+    }
+
+    //??? B3 seems to suggest that we should create an abstract class
+    //public void testB3() throws IOException, ReflectiveOperationException {
+    //    compileSpec("Ba1(Cc0)");
+    //    assertLinkage("B", "C0", "A1");
+    //}
+
+    // B4 needs too many classes
+
+    public void testB5() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc1(Aa1(Id0))");
+        assertLinkage("C", "C1", "C1");
+    }
+
+    public void testB6() throws IOException, ReflectiveOperationException {
+        compileSpec("C(Ac1(Id0))");
+        assertLinkage("C", "A1", "A1");
+    }
+
+    public void testB7() throws IOException, ReflectiveOperationException {
+        compileSpec("Cc1(Id0)");
+        assertLinkage("C", "C1", "C1");
+    }
+
+    public void testB8() throws IOException, ReflectiveOperationException {
+        compileSpec("C(Jd1(Id0))");
+        assertLinkage("C", "J1", "J1");
+    }
+
+    // B9 needs too many classes
+
+    // The rest of Dan's tests need generics
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/bridge/template_tests/TEST.properties	Mon Sep 09 17:11:55 2013 -0400
@@ -0,0 +1,7 @@
+# This file identifies root(s) of the test-ng hierarchy.
+
+
+
+TestNG.dirs = .
+
+lib.dirs = /lib/combo