--- a/langtools/test/tools/javac/MethodParameters.java Tue Feb 19 20:53:35 2013 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,344 +0,0 @@
-/*
- * 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.
- */
-
-/*
- * @test
- * @bug 8004727
- * @summary javac should generate method parameters correctly.
- */
-// key: opt.arg.parameters
-import com.sun.tools.classfile.*;
-import com.sun.tools.javac.file.JavacFileManager;
-import com.sun.tools.javac.main.Main;
-import com.sun.tools.javac.util.Context;
-import com.sun.tools.javac.util.Name;
-import com.sun.tools.javac.util.Names;
-import java.io.*;
-import javax.lang.model.element.*;
-import java.util.*;
-
-public class MethodParameters {
-
- static final String Foo_name = "Foo";
- static final String Foo_contents =
- "public class Foo {\n" +
- " Foo() {}\n" +
- " void foo0() {}\n" +
- " void foo2(int j, int k) {}\n" +
- "}";
- static final String Bar_name = "Bar";
- static final String Bar_contents =
- "public class Bar {\n" +
- " Bar(int i) {}" +
- " Foo foo() { return new Foo(); }\n" +
- "}";
- static final String Baz_name = "Baz";
- static final String Baz_contents =
- "public class Baz {\n" +
- " int baz;" +
- " Baz(int i) {}" +
- "}";
- static final String Qux_name = "Qux";
- static final String Qux_contents =
- "public class Qux extends Baz {\n" +
- " Qux(int i) { super(i); }" +
- "}";
- static final File classesdir = new File("methodparameters");
-
- public static void main(String... args) throws Exception {
- new MethodParameters().run();
- }
-
- void run() throws Exception {
- classesdir.mkdir();
- final File Foo_java =
- writeFile(classesdir, Foo_name + ".java", Foo_contents);
- final File Bar_java =
- writeFile(classesdir, Bar_name + ".java", Bar_contents);
- final File Baz_java =
- writeFile(classesdir, Baz_name + ".java", Baz_contents);
- System.err.println("Test compile with -parameter");
- compile("-parameters", "-d", classesdir.getPath(), Foo_java.getPath());
- // First test: make sure javac doesn't choke to death on
- // MethodParameter attributes
- System.err.println("Test compile with classfile containing MethodParameter attributes");
- compile("-parameters", "-d", classesdir.getPath(),
- "-cp", classesdir.getPath(), Bar_java.getPath());
- System.err.println("Examine class foo");
- checkFoo();
- checkBar();
- System.err.println("Test debug information conflict");
- compile("-g", "-parameters", "-d", classesdir.getPath(),
- "-cp", classesdir.getPath(), Baz_java.getPath());
- System.err.println("Introducing debug information conflict");
- Baz_java.delete();
- modifyBaz(false);
- System.err.println("Checking language model");
- inspectBaz();
- System.err.println("Permuting attributes");
- modifyBaz(true);
- System.err.println("Checking language model");
- inspectBaz();
-
- if(0 != errors)
- throw new Exception("MethodParameters test failed with " +
- errors + " errors");
- }
-
- void inspectBaz() throws Exception {
- final File Qux_java =
- writeFile(classesdir, Qux_name + ".java", Qux_contents);
- final String[] args = { "-XDsave-parameter-names", "-d",
- classesdir.getPath(),
- "-cp", classesdir.getPath(),
- Qux_java.getPath() };
- final StringWriter sw = new StringWriter();
- final PrintWriter pw = new PrintWriter(sw);
-
- // We need to be able to crack open javac and look at its data
- // structures. We'll rig up a compiler instance, but keep its
- // Context, thus allowing us to get at the ClassReader.
- Context context = new Context();
- Main comp = new Main("javac", pw);
- JavacFileManager.preRegister(context);
-
- // Compile Qux, which uses Baz.
- comp.compile(args, context);
- pw.close();
- final String out = sw.toString();
- if (out.length() > 0)
- System.err.println(out);
-
- // Now get the class reader, construct a name for Baz, and load it.
- com.sun.tools.javac.jvm.ClassReader cr =
- com.sun.tools.javac.jvm.ClassReader.instance(context);
- Name name = Names.instance(context).fromString(Baz_name);
-
- // Now walk down the language model and check the name of the
- // parameter.
- final Element baz = cr.loadClass(name);
- for (Element e : baz.getEnclosedElements()) {
- if (e instanceof ExecutableElement) {
- final ExecutableElement ee = (ExecutableElement) e;
- final List<? extends VariableElement> params =
- ee.getParameters();
- if (1 != params.size())
- throw new Exception("Classfile Baz badly formed: wrong number of methods");
- final VariableElement param = params.get(0);
- if (!param.getSimpleName().contentEquals("baz")) {
- errors++;
- System.err.println("javac did not correctly resolve the metadata conflict, parameter's name reads as " + param.getSimpleName());
- } else
- System.err.println("javac did correctly resolve the metadata conflict");
- }
- }
- }
-
- void modifyBaz(boolean flip) throws Exception {
- final File Baz_class = new File(classesdir, Baz_name + ".class");
- final ClassFile baz = ClassFile.read(Baz_class);
- final int ind = baz.constant_pool.getUTF8Index("baz");
- MethodParameters_attribute mpattr = null;
- int mpind = 0;
- Code_attribute cattr = null;
- int cind = 0;
-
- // Find the indexes of the MethodParameters and the Code attributes
- if (baz.methods.length != 1)
- throw new Exception("Classfile Baz badly formed: wrong number of methods");
- if (!baz.methods[0].getName(baz.constant_pool).equals("<init>"))
- throw new Exception("Classfile Baz badly formed: method has name " +
- baz.methods[0].getName(baz.constant_pool));
- for (int i = 0; i < baz.methods[0].attributes.attrs.length; i++) {
- if (baz.methods[0].attributes.attrs[i] instanceof
- MethodParameters_attribute) {
- mpattr = (MethodParameters_attribute)
- baz.methods[0].attributes.attrs[i];
- mpind = i;
- } else if (baz.methods[0].attributes.attrs[i] instanceof
- Code_attribute) {
- cattr = (Code_attribute) baz.methods[0].attributes.attrs[i];
- cind = i;
- }
- }
- if (null == mpattr)
- throw new Exception("Classfile Baz badly formed: no method parameters info");
- if (null == cattr)
- throw new Exception("Classfile Baz badly formed: no local variable table");
-
- int flags = mpattr.method_parameter_table[0].flags;
-
- // Alter the MethodParameters attribute, changing the name of
- // the parameter from i to baz. This requires Black Magic...
- //
- // The (well-designed) classfile library (correctly) does not
- // allow us to mess around with the attribute data structures,
- // or arbitrarily generate new ones.
- //
- // Instead, we install a new subclass of Attribute that
- // hijacks the Visitor pattern and outputs the sequence of
- // bytes that we want. This only works in this particular
- // instance, because we know we'll only every see one kind of
- // visitor.
- //
- // If anyone ever changes the makeup of the Baz class, or
- // tries to install some kind of visitor that gets run prior
- // to serialization, this will break.
- baz.methods[0].attributes.attrs[mpind] =
- new Attribute(mpattr.attribute_name_index,
- mpattr.attribute_length) {
- public <R, D> R accept(Visitor<R, D> visitor, D data) {
- if (data instanceof ByteArrayOutputStream) {
- ByteArrayOutputStream out =
- (ByteArrayOutputStream) data;
- out.write(1);
- out.write((ind >> 8) & 0xff);
- out.write(ind & 0xff);
- out.write((flags >> 24) & 0xff);
- out.write((flags >> 16) & 0xff);
- out.write((flags >> 8) & 0xff);
- out.write(flags & 0xff);
- } else
- throw new RuntimeException("Output stream is of type " + data.getClass() + ", which is not handled by this test. Update the test and it should work.");
- return null;
- }
- };
-
- // Flip the code and method attributes. This is for checking
- // that order doesn't matter.
- if (flip) {
- baz.methods[0].attributes.attrs[mpind] = cattr;
- baz.methods[0].attributes.attrs[cind] = mpattr;
- }
-
- new ClassWriter().write(baz, Baz_class);
- }
-
- // Run a bunch of structural tests on foo to make sure it looks right.
- void checkFoo() throws Exception {
- final File Foo_class = new File(classesdir, Foo_name + ".class");
- final ClassFile foo = ClassFile.read(Foo_class);
- for (int i = 0; i < foo.methods.length; i++) {
- System.err.println("Examine method Foo." + foo.methods[i].getName(foo.constant_pool));
- if (foo.methods[i].getName(foo.constant_pool).equals("foo2")) {
- for (int j = 0; j < foo.methods[i].attributes.attrs.length; j++)
- if (foo.methods[i].attributes.attrs[j] instanceof
- MethodParameters_attribute) {
- MethodParameters_attribute mp =
- (MethodParameters_attribute)
- foo.methods[i].attributes.attrs[j];
- System.err.println("Foo.foo2 should have 2 parameters: j and k");
- if (2 != mp.method_parameter_table_length)
- error("expected 2 method parameter entries in foo2, got " +
- mp.method_parameter_table_length);
- else if (!foo.constant_pool.getUTF8Value(mp.method_parameter_table[0].name_index).equals("j"))
- error("expected first parameter to foo2 to be \"j\", got \"" +
- foo.constant_pool.getUTF8Value(mp.method_parameter_table[0].name_index) +
- "\" instead");
- else if (!foo.constant_pool.getUTF8Value(mp.method_parameter_table[1].name_index).equals("k"))
- error("expected first parameter to foo2 to be \"k\", got \"" +
- foo.constant_pool.getUTF8Value(mp.method_parameter_table[1].name_index) +
- "\" instead");
- }
- }
- else if (foo.methods[i].getName(foo.constant_pool).equals("<init>")) {
- for (int j = 0; j < foo.methods[i].attributes.attrs.length; j++) {
- if (foo.methods[i].attributes.attrs[j] instanceof
- MethodParameters_attribute)
- error("Zero-argument constructor shouldn't have MethodParameters");
- }
- }
- else if (foo.methods[i].getName(foo.constant_pool).equals("foo0")) {
- for (int j = 0; j < foo.methods[i].attributes.attrs.length; j++)
- if (foo.methods[i].attributes.attrs[j] instanceof
- MethodParameters_attribute)
- error("Zero-argument method shouldn't have MethodParameters");
- }
- else
- error("Unknown method " + foo.methods[i].getName(foo.constant_pool) + " showed up in class Foo");
- }
- }
-
- // Run a bunch of structural tests on Bar to make sure it looks right.
- void checkBar() throws Exception {
- final File Bar_class = new File(classesdir, Bar_name + ".class");
- final ClassFile bar = ClassFile.read(Bar_class);
- for (int i = 0; i < bar.methods.length; i++) {
- System.err.println("Examine method Bar." + bar.methods[i].getName(bar.constant_pool));
- if (bar.methods[i].getName(bar.constant_pool).equals("<init>")) {
- for (int j = 0; j < bar.methods[i].attributes.attrs.length; j++)
- if (bar.methods[i].attributes.attrs[j] instanceof
- MethodParameters_attribute) {
- MethodParameters_attribute mp =
- (MethodParameters_attribute)
- bar.methods[i].attributes.attrs[j];
- System.err.println("Bar constructor should have 1 parameter: i");
- if (1 != mp.method_parameter_table_length)
- error("expected 1 method parameter entries in constructor, got " +
- mp.method_parameter_table_length);
- else if (!bar.constant_pool.getUTF8Value(mp.method_parameter_table[0].name_index).equals("i"))
- error("expected first parameter to foo2 to be \"i\", got \"" +
- bar.constant_pool.getUTF8Value(mp.method_parameter_table[0].name_index) +
- "\" instead");
- }
- }
- else if (bar.methods[i].getName(bar.constant_pool).equals("foo")) {
- for (int j = 0; j < bar.methods[i].attributes.attrs.length; j++) {
- if (bar.methods[i].attributes.attrs[j] instanceof
- MethodParameters_attribute)
- error("Zero-argument constructor shouldn't have MethodParameters");
- }
- }
- }
- }
-
- String compile(String... args) throws Exception {
- System.err.println("compile: " + Arrays.asList(args));
- StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw);
- int rc = com.sun.tools.javac.Main.compile(args, pw);
- pw.close();
- String out = sw.toString();
- if (out.length() > 0)
- System.err.println(out);
- if (rc != 0)
- error("compilation failed, rc=" + rc);
- return out;
- }
-
- File writeFile(File dir, String path, String body) throws IOException {
- File f = new File(dir, path);
- f.getParentFile().mkdirs();
- FileWriter out = new FileWriter(f);
- out.write(body);
- out.close();
- return f;
- }
-
- void error(String msg) {
- System.err.println("Error: " + msg);
- errors++;
- }
-
- int errors;
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/MethodParameters/AnnotationTest.java Wed Feb 20 15:47:14 2013 -0800
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8006582
+ * @summary javac should generate method parameters correctly.
+ * @build Tester
+ * @compile -parameters AnnotationTest.java
+ * @run main Tester AnnotationTest
+ */
+
+import java.lang.annotation.*;
+
+/** Test that annotations do not interfere with recording of parameter names */
+class AnnotationTest {
+
+ @Repeatable(Annos.class)
+ @interface Anno {
+ Class f() default int.class;
+ }
+
+ @interface Annos { Anno[] value(); String foo() default "hello"; }
+
+ interface I {
+ int m(@Anno @Anno int i, @Anno int ji);
+ }
+
+ public AnnotationTest(@Anno @Anno I i, @Anno int ji) { }
+ public @Anno String foo(@Anno @Anno I i, int ji) { return null; }
+}
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/MethodParameters/AnonymousClass.java Wed Feb 20 15:47:14 2013 -0800
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8006582
+ * @summary javac should generate method parameters correctly.
+ * @build Tester
+ * @compile -parameters AnonymousClass.java
+ * @run main Tester AnonymousClass
+ */
+
+class AnonymousClass {
+
+ interface I<T> {
+ T m();
+ T m(T x, T yx);
+ }
+
+ private class Inner implements I<String> {
+ public Inner() { }
+ public Inner(String arg, String barg) { }
+ public String m() { return "0"; }
+ public String m(String s, String ts) { return "0"; }
+ }
+
+ public static class Sinner implements I<Long> {
+ public Sinner() { }
+ public Sinner(Long arg, Long barg) { }
+ public Long m() { return 0L; }
+ public Long m(Long s, Long ts) { return s + ts; }
+ }
+
+ /** Inner class in constructor context */
+ public AnonymousClass(final Long a, Long ba) {
+ new I<Long>() {
+ public Long m() { return null; }
+ public Long m(Long i, Long ji) { return i + ji; }
+ }.m(a, ba);
+ new Inner() {
+ public String m() { return null; }
+ public String m(String i, String ji) { return i + ji; }
+ }.m(a.toString(), ba.toString());
+ new Inner(a.toString(), ba.toString()) {
+ public String m() { return null; }
+ public String m(String i, String ji) { return i + ji; }
+ }.m(a.toString(), ba.toString());
+ new Sinner() {
+ public Long m() { return null; }
+ public Long m(Long i, Long ji) { return i + ji; }
+ }.m(a, ba);
+ new Sinner(a, ba) {
+ public Long m() { return null; }
+ public Long m(Long i, Long ji) { return i + ji; }
+ }.m(a, ba);
+ }
+
+ /** Inner class in method context */
+ public void foo(final Long a, Long ba) {
+ new I<Long>() {
+ public Long m() { return null; }
+ public Long m(Long i, Long ji) { return i + ji; }
+ }.m(a, ba);
+ new Inner() {
+ public String m() { return null; }
+ public String m(String i, String ji) { return i + ji; }
+ }.m(a.toString(), ba.toString());
+ new Inner(a.toString(), ba.toString()) {
+ public String m() { return null; }
+ public String m(String i, String ji) { return i + ji; }
+ }.m(a.toString(), ba.toString());
+ new Sinner() {
+ public Long m() { return null; }
+ public Long m(Long i, Long ji) { return i + ji; }
+ }.m(a, ba);
+ new Sinner(a, ba) {
+ public Long m() { return null; }
+ public Long m(Long i, Long ji) { return i + ji; }
+ }.m(a, ba);
+ }
+}
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/MethodParameters/AttributeVisitor.java Wed Feb 20 15:47:14 2013 -0800
@@ -0,0 +1,59 @@
+/*
+ * 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 com.sun.tools.classfile.*;
+
+/**
+ * Trivial {@code Attribute.Visitor} implementation, to make it easy to
+ * write visitors for specific attributes.
+ */
+class AttributeVisitor<R, P> implements Attribute.Visitor<R, P> {
+ public R visitBootstrapMethods(BootstrapMethods_attribute attr, P p) { return null; }
+ public R visitDefault(DefaultAttribute attr, P p) { return null; }
+ public R visitAnnotationDefault(AnnotationDefault_attribute attr, P p) { return null; }
+ public R visitCharacterRangeTable(CharacterRangeTable_attribute attr, P p) { return null; }
+ public R visitCode(Code_attribute attr, P p) { return null; }
+ public R visitCompilationID(CompilationID_attribute attr, P p) { return null; }
+ public R visitConstantValue(ConstantValue_attribute attr, P p) { return null; }
+ public R visitDeprecated(Deprecated_attribute attr, P p) { return null; }
+ public R visitEnclosingMethod(EnclosingMethod_attribute attr, P p) { return null; }
+ public R visitExceptions(Exceptions_attribute attr, P p) { return null; }
+ public R visitInnerClasses(InnerClasses_attribute attr, P p) { return null; }
+ public R visitLineNumberTable(LineNumberTable_attribute attr, P p) { return null; }
+ public R visitLocalVariableTable(LocalVariableTable_attribute attr, P p) { return null; }
+ public R visitLocalVariableTypeTable(LocalVariableTypeTable_attribute attr, P p) { return null; }
+ public R visitMethodParameters(MethodParameters_attribute attr, P p) { return null; }
+ public R visitRuntimeVisibleAnnotations(RuntimeVisibleAnnotations_attribute attr, P p) { return null; }
+ public R visitRuntimeInvisibleAnnotations(RuntimeInvisibleAnnotations_attribute attr, P p) { return null; }
+ public R visitRuntimeVisibleParameterAnnotations(RuntimeVisibleParameterAnnotations_attribute attr, P p) { return null; }
+ public R visitRuntimeInvisibleParameterAnnotations(RuntimeInvisibleParameterAnnotations_attribute attr, P p) { return null; }
+ public R visitRuntimeVisibleTypeAnnotations(RuntimeVisibleTypeAnnotations_attribute attr, P p) { return null; }
+ public R visitRuntimeInvisibleTypeAnnotations(RuntimeInvisibleTypeAnnotations_attribute attr, P p) { return null; }
+ public R visitSignature(Signature_attribute attr, P p) { return null; }
+ public R visitSourceDebugExtension(SourceDebugExtension_attribute attr, P p) { return null; }
+ public R visitSourceFile(SourceFile_attribute attr, P p) { return null; }
+ public R visitSourceID(SourceID_attribute attr, P p) { return null; }
+ public R visitStackMap(StackMap_attribute attr, P p) { return null; }
+ public R visitStackMapTable(StackMapTable_attribute attr, P p) { return null; }
+ public R visitSynthetic(Synthetic_attribute attr, P p) { return null; }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/MethodParameters/ClassFileVisitor.java Wed Feb 20 15:47:14 2013 -0800
@@ -0,0 +1,387 @@
+/*
+ * 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 com.sun.tools.classfile.*;
+import java.io.*;
+import javax.lang.model.element.*;
+import java.util.*;
+
+/**
+ * The {@code ClassFileVisitor} reads a class file using the
+ * {@code com.sun.tools.classfile} library. It iterates over the methods
+ * in a class, and checks MethodParameters attributes against JLS
+ * requirements, as well as assumptions about the javac implementations.
+ * <p>
+ * It enforces the following rules:
+ * <ul>
+ * <li>All non-synthetic methods with arguments must have the
+ * MethodParameters attribute. </li>
+ * <li>At most one MethodParameters attribute per method.</li>
+ * <li>An empty MethodParameters attribute is not allowed (i.e. no
+ * attribute for methods taking no parameters).</li>
+ * <li>The number of recorded parameter names much equal the number
+ * of parameters, including any implicit or synthetic parameters generated
+ * by the compiler.</li>
+ * <li>Although the spec allow recording parameters with no name, the javac
+ * implementation is assumed to record a name for all parameters. That is,
+ * the Methodparameters attribute must record a non-zero, valid constant
+ * pool index for each parameter.</li>
+ * <li>Check presence, expected names (e.g. this$N, $enum$name, ...) and flags
+ * (e.g. ACC_SYNTHETIC, ACC_MANDATED) for compiler generated parameters.</li>
+ * <li>Names of explicit parameters must reflect the names in the Java source.
+ * This is checked by assuming a design pattern where any name is permitted
+ * for the first explicit parameter. For subsequent parameters the following
+ * rule is checked: <i>param[n] == ++param[n-1].charAt(0) + param[n-1]</i>
+ * </ul>
+ */
+class ClassFileVisitor extends Tester.Visitor {
+
+ Tester tester;
+
+ public String cname;
+ public boolean isEnum;
+ public boolean isInterface;
+ public boolean isInner;
+ public boolean isPublic;
+ public boolean isStatic;
+ public boolean isAnon;
+ public ClassFile classFile;
+
+
+ public ClassFileVisitor(Tester tester) {
+ super(tester);
+ }
+
+ public void error(String msg) {
+ super.error("classfile: " + msg);
+ }
+
+ public void warn(String msg) {
+ super.warn("classfile: " + msg);
+ }
+
+ /**
+ * Read the class and determine some key characteristics, like if it's
+ * an enum, or inner class, etc.
+ */
+ void visitClass(final String cname, final File cfile, final StringBuilder sb)
+ throws Exception {
+ this.cname = cname;
+ classFile = ClassFile.read(cfile);
+ isEnum = classFile.access_flags.is(AccessFlags.ACC_ENUM);
+ isInterface = classFile.access_flags.is(AccessFlags.ACC_INTERFACE);
+ isPublic = classFile.access_flags.is(AccessFlags.ACC_PUBLIC);
+ isInner = false;
+ isStatic = true;
+ isAnon = false;
+
+ Attribute attr = classFile.getAttribute("InnerClasses");
+ if (attr != null) attr.accept(new InnerClassVisitor(), null);
+ isAnon = isInner & isAnon;
+
+ sb.append(isStatic ? "static " : "")
+ .append(isPublic ? "public " : "")
+ .append(isEnum ? "enum " : isInterface ? "interface " : "class ")
+ .append(cname).append(" -- ")
+ .append(isInner? "inner " : "" )
+ .append(isAnon ? "anon" : "")
+ .append("\n");;
+
+ for (Method method : classFile.methods) {
+ new MethodVisitor().visitMethod(method, sb);
+ }
+ }
+
+ /**
+ * Used to visit InnerClasses_attribute of a class,
+ * to determne if this class is an local class, and anonymous
+ * inner class or a none-static member class. These types of
+ * classes all have an containing class instances field that
+ * requires an implicit or synthetic constructor argument.
+ */
+ class InnerClassVisitor extends AttributeVisitor<Void, Void> {
+ public Void visitInnerClasses(InnerClasses_attribute iattr, Void v) {
+ try{
+ for (InnerClasses_attribute.Info info : iattr.classes) {
+ if (info.getInnerClassInfo(classFile.constant_pool) == null) continue;
+ String in = info.getInnerClassInfo(classFile.constant_pool).getName();
+ if (in == null || !cname.equals(in)) continue;
+ isInner = true;
+ isAnon = null == info.getInnerName(classFile.constant_pool);
+ isStatic = info.inner_class_access_flags.is(AccessFlags.ACC_STATIC);
+ break;
+ }
+ } catch(Exception e) {
+ throw new IllegalStateException(e);
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Check the MethodParameters attribute of a method.
+ */
+ class MethodVisitor extends AttributeVisitor<Void, StringBuilder> {
+
+ public String mName;
+ public Descriptor mDesc;
+ public int mParams;
+ public int mAttrs;
+ public int mNumParams;
+ public boolean mSynthetic;
+ public boolean mIsConstructor;
+ public String prefix;
+
+ void visitMethod(Method method, StringBuilder sb) throws Exception {
+
+ mName = method.getName(classFile.constant_pool);
+ mDesc = method.descriptor;
+ mParams = mDesc.getParameterCount(classFile.constant_pool);
+ mAttrs = method.attributes.attrs.length;
+ mNumParams = -1; // no MethodParameters attribute found
+ mSynthetic = method.access_flags.is(AccessFlags.ACC_SYNTHETIC);
+ mIsConstructor = mName.equals("<init>");
+ prefix = cname + "." + mName + "() - ";
+
+ sb.append(cname).append(".").append(mName).append("(");
+
+ for (Attribute a : method.attributes) {
+ a.accept(this, sb);
+ }
+ if (mNumParams == -1) {
+ if (mSynthetic) {
+ sb.append("<none>)!!");
+ } else {
+ sb.append("<none>)");
+ }
+ }
+ sb.append("\n");
+
+ // IMPL: methods with arguments must have a MethodParameters
+ // attribute, except possibly some synthetic methods.
+ if (mNumParams == -1 && mParams > 0 && ! mSynthetic) {
+ error(prefix + "missing MethodParameters attribute");
+ }
+ }
+
+ public Void visitMethodParameters(MethodParameters_attribute mp,
+ StringBuilder sb) {
+
+ // SPEC: At most one MethodParameters attribute allowed
+ if (mNumParams != -1) {
+ error(prefix + "Multiple MethodParameters attributes");
+ return null;
+ }
+
+ mNumParams = mp.method_parameter_table_length;
+
+ // SPEC: An empty attribute is not allowed!
+ if (mNumParams == 0) {
+ error(prefix + "0 length MethodParameters attribute");
+ return null;
+ }
+
+ // SPEC: one name per parameter.
+ if (mNumParams != mParams) {
+ error(prefix + "found " + mNumParams +
+ " parameters, expected " + mParams);
+ return null;
+ }
+
+ // IMPL: Whether MethodParameters attributes will be generated
+ // for some synthetics is unresolved. For now, assume no.
+ if (mSynthetic) {
+ warn(prefix + "synthetic has MethodParameter attribute");
+ }
+
+ String sep = "";
+ String userParam = null;
+ for (int x = 0; x < mNumParams; x++) {
+
+ // IMPL: Assume all parameters are named, something.
+ int cpi = mp.method_parameter_table[x].name_index;
+ if (cpi == 0) {
+ error(prefix + "name expected, param[" + x + "]");
+ return null;
+ }
+
+ // SPEC: a non 0 index, must be valid!
+ String param = null;
+ try {
+ param = classFile.constant_pool.getUTF8Value(cpi);
+ sb.append(sep).append(param);
+ sep = ", ";
+ } catch(ConstantPoolException e) {
+ error(prefix + "invalid index " + cpi + " for param["
+ + x + "]");
+ return null;
+ }
+
+
+ // Check availability, flags and special names
+ int check = checkParam(mp, param, x, sb);
+ if (check < 0) {
+ return null;
+ }
+
+ // TEST: check test assumptions about parameter name.
+ // Expected names are calculated starting with the
+ // 2nd explicit (user given) parameter.
+ // param[n] == ++param[n-1].charAt(0) + param[n-1]
+ String expect = null;
+ if (userParam != null) {
+ char c = userParam.charAt(0);
+ expect = (++c) + userParam;
+ }
+ if (check > 0) {
+ userParam = param;
+ }
+ if (expect != null && !param.equals(expect)) {
+ error(prefix + "param[" + x + "]='"
+ + param + "' expected '" + expect + "'");
+ return null;
+ }
+ }
+ if (mSynthetic) {
+ sb.append(")!!");
+ } else {
+ sb.append(")");
+ }
+ return null;
+ }
+
+ /*
+ * Check a parameter for conformity to JLS and javac specific
+ * assumptions.
+ * Return -1, if an error is detected. Otherwise, return 0, if
+ * the parameter is compiler generated, or 1 for an (presumably)
+ * explicitly declared parameter.
+ */
+ int checkParam(MethodParameters_attribute mp, String param, int index,
+ StringBuilder sb) {
+
+ boolean synthetic = (mp.method_parameter_table[index].flags
+ & AccessFlags.ACC_SYNTHETIC) != 0;
+ boolean mandated = (mp.method_parameter_table[index].flags
+ & AccessFlags.ACC_MANDATED) != 0;
+
+ // Setup expectations for flags and special names
+ String expect = null;
+ boolean allowMandated = false;
+ boolean allowSynthetic = false;
+ if (mSynthetic || synthetic) {
+ // not an implementation gurantee, but okay for now
+ expect = "arg" + index; // default
+ }
+ if (mIsConstructor) {
+ if (isEnum) {
+ if (index == 0) {
+ expect = "\\$enum\\$name";
+ allowSynthetic = true;
+ } else if(index == 1) {
+ expect = "\\$enum\\$ordinal";
+ allowSynthetic = true;
+ }
+ } else if (index == 0) {
+ if (isAnon) {
+ allowMandated = true;
+ expect = "this\\$[0-n]*";
+ } else if (isInner && !isStatic) {
+ allowMandated = true;
+ if (!isPublic) {
+ // some but not all non-public inner classes
+ // have synthetic argument. For now we give
+ // the test a bit of slack and allow either.
+ allowSynthetic = true;
+ }
+ expect = "this\\$[0-n]*";
+ }
+ } else if (isAnon) {
+ // not an implementation gurantee, but okay for now
+ expect = "x[0-n]*";
+ }
+ } else if (isEnum && mNumParams == 1 && index == 0 && mName.equals("valueOf")) {
+ expect = "name";
+ allowMandated = true;
+ }
+ if (mandated) sb.append("!");
+ if (synthetic) sb.append("!!");
+
+ // IMPL: our rules a somewhat fuzzy, sometimes allowing both mandated
+ // and synthetic. However, a parameters cannot be both.
+ if (mandated && synthetic) {
+ error(prefix + "param[" + index + "] == \"" + param
+ + "\" ACC_SYNTHETIC and ACC_MANDATED");
+ return -1;
+ }
+ // ... but must be either, if both "allowed".
+ if (!(mandated || synthetic) && allowMandated && allowSynthetic) {
+ error(prefix + "param[" + index + "] == \"" + param
+ + "\" expected ACC_MANDATED or ACC_SYNTHETIC");
+ return -1;
+ }
+
+ // ... if only one is "allowed", we meant "required".
+ if (!mandated && allowMandated && !allowSynthetic) {
+ error(prefix + "param[" + index + "] == \"" + param
+ + "\" expected ACC_MANDATED");
+ return -1;
+ }
+ if (!synthetic && !allowMandated && allowSynthetic) {
+ error(prefix + "param[" + index + "] == \"" + param
+ + "\" expected ACC_SYNTHETIC");
+ return -1;
+ }
+
+ // ... and not "allowed", means prohibited.
+ if (mandated && !allowMandated) {
+ error(prefix + "param[" + index + "] == \"" + param
+ + "\" unexpected, is ACC_MANDATED");
+ return -1;
+ }
+ if (synthetic && !allowSynthetic) {
+ error(prefix + "param[" + index + "] == \"" + param
+ + "\" unexpected, is ACC_SYNTHETIC");
+ return -1;
+ }
+
+ // Test special name expectations
+ if (expect != null) {
+ if (param.matches(expect)) {
+ return 0;
+ }
+ error(prefix + "param[" + index + "]='" + param +
+ "' expected '" + expect + "'");
+ return -1;
+ }
+
+ // No further checking for synthetic methods.
+ if (mSynthetic) {
+ return 0;
+ }
+
+ // Otherwise, do check test parameter naming convention.
+ return 1;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/MethodParameters/Constructors.java Wed Feb 20 15:47:14 2013 -0800
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8006582
+ * @summary javac should generate method parameters correctly.
+ * @build Tester
+ * @compile -parameters Constructors.java
+ * @run main Tester Constructors
+ */
+
+public class Constructors {
+ public Constructors() {}
+ Constructors(final Object a, final String... ba) { }
+ protected Constructors(Object a, final Object ba, final String... cba) { }
+ private Constructors(int a, Object ba, final Object cba, final String... dcba) { }
+}
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/MethodParameters/EnumTest.java Wed Feb 20 15:47:14 2013 -0800
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8006582
+ * @summary javac should generate method parameters correctly.
+ * @build Tester
+ * @compile -parameters EnumTest.java
+ * @run main Tester EnumTest
+ */
+
+/** Test that parameter names are recorded for enum methods */
+enum EnumTest {
+ E1(0), E2(1, "x"), E3(2, "x", "y"), E4;
+
+ EnumTest() { }
+ EnumTest(int a, String... ba) { }
+ boolean ok(int c, String... dc) { return true; }
+
+ int valueOf(EnumTest A, EnumTest BA) { return 0; }
+}
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/MethodParameters/InstanceMethods.java Wed Feb 20 15:47:14 2013 -0800
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8006582
+ * @summary javac should generate method parameters correctly.
+ * @build Tester
+ * @compile -parameters InstanceMethods.java
+ * @run main Tester InstanceMethods
+ */
+
+public class InstanceMethods {
+ public void empty() {}
+ final void def(Object a, final Object ba, final String... cba) { }
+ final public void pub(Object d, final Object ed, final String... fed) { }
+ protected boolean prot(Object g, final Object hg, final String... ihg) { return true; }
+ private boolean priv(Object j, final Object kj, final String... lkj) { return true; }
+ void def(int A, Object BA, final Object CBA, final String... DCBA) { }
+ public void pub(int B, Object CB, final Object DCB, final String... EDCB) { }
+ final protected boolean prot(int C, Object DC, final Object EDC, final String... FEDC) { return true; }
+ final private boolean priv(int D, Object ED, final Object FED, final String... GFED) { return true; }
+}
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/MethodParameters/LambdaTest.java Wed Feb 20 15:47:14 2013 -0800
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8006582
+ * @summary javac should generate method parameters correctly.
+ * @build Tester
+ * @compile -parameters LambdaTest.java
+ * @run main Tester LambdaTest
+ */
+
+/**
+ * Parameter names are not recorded for lambdas. This test verifies
+ * that there are no MethodParameters attribute for lambdas.
+ */
+class LambdaTest {
+
+ interface I {
+ int m(int x);
+ }
+
+ static int foo(I i) { return i.m(0); }
+
+ static {
+ foo((int x1) -> { return foo((int x2) -> { return x1 + x2; }); });
+ }
+}
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/MethodParameters/LocalClassTest.java Wed Feb 20 15:47:14 2013 -0800
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8006582
+ * @summary javac should generate method parameters correctly.
+ * @build Tester
+ * @compile -parameters LocalClassTest.java
+ * @run main Tester LocalClassTest
+ */
+
+class LocalClassTest {
+ void foo() {
+ class Local_default_constructor {
+ public void foo() {}
+ public void foo(int m, int nm) {}
+ }
+ class Local_has_constructor {
+ public Local_has_constructor() {}
+ public Local_has_constructor(int a, int ba) {}
+ public void foo() {}
+ public void foo(int m, int nm) {}
+ }
+ new LocalClassTest().foo();
+ }
+}
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/MethodParameters/MemberClassTest.java Wed Feb 20 15:47:14 2013 -0800
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8006582
+ * @summary javac should generate method parameters correctly.
+ * @build Tester
+ * @compile -parameters MemberClassTest.java
+ * @run main Tester MemberClassTest
+ */
+
+class MemberClassTest {
+
+ interface I {
+ Long m();
+ Long m(Long x, Long yx);
+ }
+
+ public class Member implements I {
+ public class Member_Member {
+ public Member_Member() {}
+ public Member_Member(String x, String yx) {}
+ }
+
+ public Member() { }
+ public Member(Long a, Long ba) { }
+ public Long m() { return 0L; }
+ public Long m(Long s, Long ts) { return 0L; }
+ }
+
+ static class Static_Member implements I {
+ public class Static_Member_Member {
+ public Static_Member_Member() {}
+ public Static_Member_Member(String x, String yx) {}
+ }
+
+ public static class Static_Member_Static_Member {
+ public Static_Member_Static_Member() {}
+ public Static_Member_Static_Member(String x, String yx) {}
+ }
+ public Static_Member() { }
+ public Static_Member(Long arg, Long barg) { }
+ public Long m() { return 0L; }
+ public Long m(Long s, Long ts) { return s + ts; }
+ }
+
+ public MemberClassTest() {
+ }
+ public MemberClassTest(final Long a, Long ba) {
+ }
+
+ public void foo() {
+
+ new I() {
+
+ class Anonymous_Member {
+ public Anonymous_Member() {}
+ public Anonymous_Member(String x, String yx) {}
+ }
+
+ public Long m() { return 0L; }
+ public Long m(Long s, Long ts) { return s + ts; }
+ }.m();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/MethodParameters/ReflectionVisitor.java Wed Feb 20 15:47:14 2013 -0800
@@ -0,0 +1,270 @@
+/*
+ * 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.*;
+import java.util.*;
+import java.net.*;
+import java.lang.reflect.*;
+
+/**
+ * Test MethodParameter attributs by reflection API
+ */
+public class ReflectionVisitor extends Tester.Visitor {
+
+ public ReflectionVisitor(Tester tester) {
+ super(tester);
+ }
+
+ public void error(String msg) {
+ super.error("reflection: " + msg);
+ }
+
+ public void warn(String msg) {
+ super.warn("reflection: " + msg);
+ }
+
+ boolean isEnum;
+ boolean isInterface;
+ boolean isAnon;
+ boolean isLocal;
+ boolean isMember;
+ boolean isStatic;
+ boolean isPublic;
+ Class clazz;
+ StringBuilder sb;
+
+ /**
+ * Read class using {@code ClassFile}, and generate a list of methods
+ * with parameter names as available in the MethodParameters attribute.
+ */
+ void visitClass(final String cl, final File cfile, final StringBuilder sb)
+ throws Exception {
+
+ this.sb = sb;
+ clazz = Class.forName(cl);
+ isEnum = clazz.isEnum();
+ isInterface = clazz.isInterface();
+ isAnon = clazz.isAnonymousClass();
+ isLocal = clazz.isLocalClass();
+ isMember = clazz.isMemberClass();
+ isStatic = ((clazz.getModifiers() & Modifier.STATIC) != 0);
+ isPublic = ((clazz.getModifiers() & Modifier.PUBLIC) != 0);
+
+ sb.append(isStatic ? "static " : "")
+ .append(isPublic ? "public " : "")
+ .append(isEnum ? "enum " : isInterface ? "interface " : "class ")
+ .append(cl).append(" -- ")
+ .append(isMember? "member " : "" )
+ .append(isLocal? "local " : "" )
+ .append(isAnon ? "anon" : "")
+ .append("\n");
+
+ for (Constructor c : clazz.getDeclaredConstructors()) {
+ testConstructor(c);
+ }
+
+ for (Method m :clazz.getDeclaredMethods()) {
+
+ testMethod(m);
+ }
+ }
+
+ void testConstructor(Constructor c) {
+
+ String prefix = clazz.getName() + "." + c.getName() + "() - ";
+
+ // Parameters must match parameter types
+ Parameter params[] = c.getParameters();
+ int paramTypes = c.getParameterTypes().length;
+ if (paramTypes != params.length) {
+ error(prefix + "number of parameter types (" + paramTypes
+ + ") != number of parameters (" + params.length + ")");
+ return;
+ }
+
+ sb.append(clazz.getName()).append(".").append("<init>").append("(");
+ String sep = "";
+
+ // Some paramters are expected
+ if (params.length < 2 && isEnum) {
+ error(prefix + "enum constuctor, two arguments expected");
+ } else if (params.length < 1 && (isAnon || isLocal ||
+ (isMember && !isStatic ))) {
+ error(prefix + "class constuctor,expected implicit argument");
+ }
+
+ int i = -1;
+ String param = null;
+ for (Parameter p : c.getParameters()) {
+ i++;
+ String pname = p.getName();
+ sb.append(sep).append(pname);
+ if (p.isImplicit()) sb.append("!");
+ if (p.isSynthetic()) sb.append("!!");
+ sep = ", ";
+
+ // Set expectations
+ String expect = null;
+ boolean allowImplicit = false;
+ boolean allowSynthetic = false;
+ if (isEnum) {
+ if (i == 0) {
+ expect = "\\$enum\\$name";
+ allowSynthetic = true;
+ } else if(i == 1) {
+ expect = "\\$enum\\$ordinal";
+ allowSynthetic = true;
+ }
+ } else if (i == 0) {
+ if (isAnon) {
+ allowImplicit = true;
+ } else if (isLocal) {
+ allowImplicit = true;
+ expect = "this\\$[0-n]*";
+ } else if ((isMember && !isStatic)) {
+ allowImplicit = true;
+ if (!isPublic) {
+ // some but not all non-public inner classes
+ // have synthetic argument. For now we give
+ // the test a bit of slack and allow either.
+ allowSynthetic = true;
+ }
+ expect = "this\\$[0-n]*";
+ }
+ } else if (isAnon) {
+ // not an implementation gurantee, but okay for now
+ expect = "x[0-n]*";
+ }
+
+ // Check expected flags
+ if (p.isSynthetic() && p.isImplicit()) {
+ error(prefix + "param[" + i + "]='" + pname +
+ "' both isImplicit() and isSynthetic()");
+ break;
+ }
+ if (allowImplicit && allowSynthetic &&
+ !(p.isSynthetic() || p.isImplicit())) {
+ error(prefix + "param[" + i + "]='" + pname +
+ "' isImplicit() or isSynthetic() expected");
+ break;
+ }
+
+ if (allowImplicit && !allowSynthetic && !p.isImplicit()) {
+ error(prefix + "param[" + i + "]='" + pname +
+ "' isImplicit() expected");
+ break;
+ }
+ if (!allowImplicit && allowSynthetic && !p.isSynthetic()) {
+ error(prefix + "param[" + i + "]='" + pname +
+ "' isSynthetic() expected");
+ break;
+ }
+
+ if (!allowImplicit && p.isImplicit()) {
+ error(prefix + "param[" + i + "]='" + pname +
+ "' isImplicit() unexpected");
+ break;
+ }
+
+ if (!allowSynthetic && p.isSynthetic()) {
+ error(prefix + "param[" + i + "]='" + pname +
+ "' isSynthetic() unexpected");
+ break;
+ }
+
+ // Check expected names
+ if (expect != null) {
+ if (pname.matches(expect)) continue;
+ error(prefix + "param[" + i + "]='" + pname +
+ "' expected '" + expect + "'");
+ break;
+ }
+
+ // Test naming convention for explicit parameters.
+ boolean fidelity = !isAnon;
+ if (param != null && fidelity) {
+ char ch = param.charAt(0);
+ expect = (++ch) + param;
+ }
+
+ if (pname != null && fidelity) {
+ param = pname;
+ }
+
+ if (expect != null && !expect.equals(pname)) {
+ error(prefix + "param[" + i + "]='" + pname +
+ "' expected '" + expect + "'");
+ break;
+ }
+ }
+ if (c.isSynthetic()) {
+ sb.append(")!!\n");
+ } else {
+ sb.append(")\n");
+ }
+ }
+
+ void testMethod(Method m) {
+
+ String prefix = clazz.getName() + "." + m.getName() + "() - ";
+
+ // Parameters must match parameter types
+ int paramTypes = m.getParameterTypes().length;
+ int params = m.getParameters().length;
+ if (paramTypes != params) {
+ error(prefix + "number of parameter types (" + paramTypes
+ + ") != number of parameters (" + params + ")");
+ return;
+ }
+
+ sb.append(clazz.getName()).append(".").append(m.getName()).append("(");
+ String sep = "";
+ String param = null;
+ int i = -1;
+ // For methods we expect all parameters to follow
+ // the test-case design pattern, except synthetic methods.
+ for (Parameter p : m.getParameters()) {
+ i++;
+ if (param == null) {
+ param = p.getName();
+ sb.append(sep).append(param);
+ } else {
+ char c = param.charAt(0);
+ String expect = m.isSynthetic() ? ("arg" + i) : ((++c) + param);
+ param = p.getName();
+ sb.append(sep).append(param);
+ if (!expect.equals(param)) {
+ error(prefix + "param[" + i + "]='"
+ + param + "' expected '" + expect + "'");
+ break;
+ }
+ }
+ sep = ", ";
+ }
+ if (m.isSynthetic()) {
+ sb.append(")!!\n");
+ } else {
+ sb.append(")\n");
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/MethodParameters/StaticMethods.java Wed Feb 20 15:47:14 2013 -0800
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8006582
+ * @summary javac should generate method parameters correctly.
+ * @build Tester
+ * @compile -parameters StaticMethods.java
+ * @run main Tester StaticMethods
+ */
+
+public class StaticMethods {
+ static public void empty() {}
+ static final void def(Object a, final Object ba, final String... cba) { }
+ static final public void pub(Object d, final Object ed, final String... fed) { }
+ static protected boolean prot(Object g, final Object hg, final String... ihg) { return true; }
+ static private boolean priv(Object j, final Object kj, final String... lkj) { return true; }
+ static void def(int a, Object ba, final Object cba, final String... dcba) { }
+ static public void pub(int a, Object ba, final Object cba , final String... dcba) { }
+ static final protected boolean prot(int aa, Object baa, final Object cbaa, final String... dcbaa) { return true; }
+ static final private boolean priv(int abc, Object babc, final Object cbabc, final String... dcbabc) { return true; }
+}
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/MethodParameters/Tester.java Wed Feb 20 15:47:14 2013 -0800
@@ -0,0 +1,162 @@
+/*
+ * 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.*;
+import java.util.*;
+import java.lang.reflect.Constructor;
+
+/**
+ * Test driver for MethodParameters testing.
+ * <p>
+ * The intended use of this driver is to run it, giving the name of
+ * a single class compiled with -parameters as argument. The driver
+ * will test the specified class, and any nested classes it finds.
+ * <p>
+ * Each class is tested in two way. By refelction, and by directly
+ * checking MethodParameters attributes in the classfile. The checking
+ * is done using two visitor classes {@link ClassFileVisitor} and
+ * {@link ReflectionVisitor}.
+ * <p>
+ * The {@code ReflectionVisitor} test logically belongs with library tests.
+ * we wish to reuse the same test-cases, so both test are committed together,
+ * under langtools. The tests, may be duplicated in the jdk repository.
+ */
+public class Tester {
+
+ final static File classesdir = new File(System.getProperty("test.classes", "."));
+
+ /**
+ * The visitor classes that does the actual checking are referenced
+ * statically, to force compilations, without having to reference
+ * them in individual test cases.
+ * <p>
+ * This makes it easy to change the set of visitors, without
+ * complicating the design with dynamic discovery and compilation
+ * of visitor classes.
+ */
+ static final Class visitors[] = {
+ ClassFileVisitor.class,
+ ReflectionVisitor.class
+ };
+
+ /**
+ * Test-driver expect a single classname as argument.
+ */
+ public static void main(String... args) throws Exception {
+ if (args.length != 1) {
+ throw new Error("A single class name is expected as argument");
+ }
+ final String pattern = args[0] + ".*\\.class";
+ File files[] = classesdir.listFiles(new FileFilter() {
+ public boolean accept(File f) {
+ return f.getName().matches(pattern);
+ }
+ });
+ if (files.length == 0) {
+ File file = new File(classesdir, args[0] + ".class");
+ throw new Error(file.getPath() + " not found");
+ }
+
+ new Tester(args[0], files).run();
+ }
+
+ public Tester(String name, File files[]) {
+ this.classname = name;
+ this.files = files;
+ }
+
+ void run() throws Exception {
+
+ // Test with each visitor
+ for (Class<Visitor> vclass : visitors) {
+ try {
+ String vname = vclass.getName();
+ Constructor c = vclass.getConstructor(Tester.class);
+
+ info("\nRun " + vname + " for " + classname + "\n");
+ StringBuilder sb = new StringBuilder();
+ for (File f : files) {
+ String fname = f.getName();
+ fname = fname.substring(0, fname.length() - 6);
+ Visitor v = (Visitor) c.newInstance(this);
+ try {
+ v.visitClass(fname, f, sb);
+ } catch(Exception e) {
+ error("Uncaught exception in visitClass()");
+ e.printStackTrace();
+ }
+ }
+ info(sb.toString());
+ } catch(ReflectiveOperationException e) {
+ warn("Class " + vclass.getName() + " ignored, not a Visitor");
+ continue;
+ }
+ }
+
+ if(0 != warnings)
+ System.err.println("Test generated " + warnings + " warnings");
+
+ if(0 != errors)
+ throw new Exception("Tester test failed with " +
+ errors + " errors");
+ }
+
+ abstract static class Visitor {
+ Tester tester;
+ File classesdir;
+
+ public Visitor(Tester tester) {
+ this.tester = tester;
+ }
+
+ abstract void visitClass(final String classname, final File cfile,
+ final StringBuilder sb) throws Exception;
+
+ public void error(String msg) {
+ tester.error(msg);
+ }
+
+ public void warn(String msg) {
+ tester.warn(msg);
+ }
+ }
+
+ void error(String msg) {
+ System.err.println("Error: " + msg);
+ errors++;
+ }
+
+ void warn(String msg) {
+ System.err.println("Warning: " + msg);
+ warnings++;
+ }
+
+ void info(String msg) {
+ System.out.println(msg);
+ }
+
+ int errors;
+ int warnings;
+ String classname;
+ File files[];
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/MethodParameters/UncommonParamNames.java Wed Feb 20 15:47:14 2013 -0800
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8006582
+ * @summary javac should generate method parameters correctly.
+ * @build Tester
+ * @compile -parameters UncommonParamNames.java
+ * @run main Tester UncommonParamNames
+ */
+
+/** Test uncommon parameter names */
+class UncommonParamNames {
+ public UncommonParamNames(int _x) { }
+ public UncommonParamNames(short $1) { }
+ public UncommonParamNames(long \u0061) { }
+ public UncommonParamNames(char zero\u0000zero\u0000) { }
+ public UncommonParamNames(String zero\u0000zero\u0000seven\u0007) { }
+ public UncommonParamNames(Object zero\u0000zero\u0000eight\u0008) { }
+ public UncommonParamNames(Object aLoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooongName,
+ Object baLoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooongName,
+ Object cbaLoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooongName) { }
+ public UncommonParamNames(int a, int ba, int cba, int dcba, int edcba, int fedcba, int gfedcba,
+ int hgfedcba, int ihgfedcba, int jihgfedcba, int kjihgfedcba, int lkjihgfedcba,
+ int mlkjihgfedcba, int nmlkjihgfedcba, int onmlkjihgfedcba, int ponmlkjihgfedcba,
+ int qponmlkjihgfedcba, int rqponmlkjihgfedcba, int srqponmlkjihgfedcba,
+ int tsrqponmlkjihgfedcba, int utsrqponmlkjihgfedcba, int vutsrqponmlkjihgfedcba,
+ int wvutsrqponmlkjihgfedcba, int xwvutsrqponmlkjihgfedcba,
+ int yxwvutsrqponmlkjihgfedcba, int zyxwvutsrqponmlkjihgfedcba) { }
+
+ public void foo(int _x) { }
+ public void foo(short $1) { }
+ public void foo(long \u0061) { }
+ public void foo(char zero\u0000zero\u0000) { }
+ public void foo(String zero\u0000zero\u0000seven\u0007) { }
+ public void foo(Object zero\u0000zero\u0000eight\u0008) { }
+ public void foo(Object aLoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooongName,
+ Object baLoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooongName,
+ Object cbaLoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooongName) { }
+ public void foo(int a, int ba, int cba, int dcba, int edcba, int fedcba, int gfedcba,
+ int hgfedcba, int ihgfedcba, int jihgfedcba, int kjihgfedcba, int lkjihgfedcba,
+ int mlkjihgfedcba, int nmlkjihgfedcba, int onmlkjihgfedcba, int ponmlkjihgfedcba,
+ int qponmlkjihgfedcba, int rqponmlkjihgfedcba, int srqponmlkjihgfedcba,
+ int tsrqponmlkjihgfedcba, int utsrqponmlkjihgfedcba, int vutsrqponmlkjihgfedcba,
+ int wvutsrqponmlkjihgfedcba, int xwvutsrqponmlkjihgfedcba,
+ int yxwvutsrqponmlkjihgfedcba, int zyxwvutsrqponmlkjihgfedcba) { }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/MethodParametersTest.java Wed Feb 20 15:47:14 2013 -0800
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+/*
+ * @test
+ * @bug 8004727
+ * @summary javac should generate method parameters correctly.
+ */
+// key: opt.arg.parameters
+import com.sun.tools.classfile.*;
+import com.sun.tools.javac.file.JavacFileManager;
+import com.sun.tools.javac.main.Main;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.Name;
+import com.sun.tools.javac.util.Names;
+import java.io.*;
+import javax.lang.model.element.*;
+import java.util.*;
+
+public class MethodParametersTest {
+
+ static final String Foo_name = "Foo";
+ static final String Foo_contents =
+ "public class Foo {\n" +
+ " Foo() {}\n" +
+ " void foo0() {}\n" +
+ " void foo2(int j, int k) {}\n" +
+ "}";
+ static final String Bar_name = "Bar";
+ static final String Bar_contents =
+ "public class Bar {\n" +
+ " Bar(int i) {}" +
+ " Foo foo() { return new Foo(); }\n" +
+ "}";
+ static final String Baz_name = "Baz";
+ static final String Baz_contents =
+ "public class Baz {\n" +
+ " int baz;" +
+ " Baz(int i) {}" +
+ "}";
+ static final String Qux_name = "Qux";
+ static final String Qux_contents =
+ "public class Qux extends Baz {\n" +
+ " Qux(int i) { super(i); }" +
+ "}";
+ static final File classesdir = new File("methodparameters");
+
+ public static void main(String... args) throws Exception {
+ new MethodParametersTest().run();
+ }
+
+ void run() throws Exception {
+ classesdir.mkdir();
+ final File Foo_java =
+ writeFile(classesdir, Foo_name + ".java", Foo_contents);
+ final File Bar_java =
+ writeFile(classesdir, Bar_name + ".java", Bar_contents);
+ final File Baz_java =
+ writeFile(classesdir, Baz_name + ".java", Baz_contents);
+ System.err.println("Test compile with -parameter");
+ compile("-parameters", "-d", classesdir.getPath(), Foo_java.getPath());
+ // First test: make sure javac doesn't choke to death on
+ // MethodParameter attributes
+ System.err.println("Test compile with classfile containing MethodParameter attributes");
+ compile("-parameters", "-d", classesdir.getPath(),
+ "-cp", classesdir.getPath(), Bar_java.getPath());
+ System.err.println("Examine class foo");
+ checkFoo();
+ checkBar();
+ System.err.println("Test debug information conflict");
+ compile("-g", "-parameters", "-d", classesdir.getPath(),
+ "-cp", classesdir.getPath(), Baz_java.getPath());
+ System.err.println("Introducing debug information conflict");
+ Baz_java.delete();
+ modifyBaz(false);
+ System.err.println("Checking language model");
+ inspectBaz();
+ System.err.println("Permuting attributes");
+ modifyBaz(true);
+ System.err.println("Checking language model");
+ inspectBaz();
+
+ if(0 != errors)
+ throw new Exception("MethodParameters test failed with " +
+ errors + " errors");
+ }
+
+ void inspectBaz() throws Exception {
+ final File Qux_java =
+ writeFile(classesdir, Qux_name + ".java", Qux_contents);
+ final String[] args = { "-XDsave-parameter-names", "-d",
+ classesdir.getPath(),
+ "-cp", classesdir.getPath(),
+ Qux_java.getPath() };
+ final StringWriter sw = new StringWriter();
+ final PrintWriter pw = new PrintWriter(sw);
+
+ // We need to be able to crack open javac and look at its data
+ // structures. We'll rig up a compiler instance, but keep its
+ // Context, thus allowing us to get at the ClassReader.
+ Context context = new Context();
+ Main comp = new Main("javac", pw);
+ JavacFileManager.preRegister(context);
+
+ // Compile Qux, which uses Baz.
+ comp.compile(args, context);
+ pw.close();
+ final String out = sw.toString();
+ if (out.length() > 0)
+ System.err.println(out);
+
+ // Now get the class reader, construct a name for Baz, and load it.
+ com.sun.tools.javac.jvm.ClassReader cr =
+ com.sun.tools.javac.jvm.ClassReader.instance(context);
+ Name name = Names.instance(context).fromString(Baz_name);
+
+ // Now walk down the language model and check the name of the
+ // parameter.
+ final Element baz = cr.loadClass(name);
+ for (Element e : baz.getEnclosedElements()) {
+ if (e instanceof ExecutableElement) {
+ final ExecutableElement ee = (ExecutableElement) e;
+ final List<? extends VariableElement> params =
+ ee.getParameters();
+ if (1 != params.size())
+ throw new Exception("Classfile Baz badly formed: wrong number of methods");
+ final VariableElement param = params.get(0);
+ if (!param.getSimpleName().contentEquals("baz")) {
+ errors++;
+ System.err.println("javac did not correctly resolve the metadata conflict, parameter's name reads as " + param.getSimpleName());
+ } else
+ System.err.println("javac did correctly resolve the metadata conflict");
+ }
+ }
+ }
+
+ void modifyBaz(boolean flip) throws Exception {
+ final File Baz_class = new File(classesdir, Baz_name + ".class");
+ final ClassFile baz = ClassFile.read(Baz_class);
+ final int ind = baz.constant_pool.getUTF8Index("baz");
+ MethodParameters_attribute mpattr = null;
+ int mpind = 0;
+ Code_attribute cattr = null;
+ int cind = 0;
+
+ // Find the indexes of the MethodParameters and the Code attributes
+ if (baz.methods.length != 1)
+ throw new Exception("Classfile Baz badly formed: wrong number of methods");
+ if (!baz.methods[0].getName(baz.constant_pool).equals("<init>"))
+ throw new Exception("Classfile Baz badly formed: method has name " +
+ baz.methods[0].getName(baz.constant_pool));
+ for (int i = 0; i < baz.methods[0].attributes.attrs.length; i++) {
+ if (baz.methods[0].attributes.attrs[i] instanceof
+ MethodParameters_attribute) {
+ mpattr = (MethodParameters_attribute)
+ baz.methods[0].attributes.attrs[i];
+ mpind = i;
+ } else if (baz.methods[0].attributes.attrs[i] instanceof
+ Code_attribute) {
+ cattr = (Code_attribute) baz.methods[0].attributes.attrs[i];
+ cind = i;
+ }
+ }
+ if (null == mpattr)
+ throw new Exception("Classfile Baz badly formed: no method parameters info");
+ if (null == cattr)
+ throw new Exception("Classfile Baz badly formed: no local variable table");
+
+ int flags = mpattr.method_parameter_table[0].flags;
+
+ // Alter the MethodParameters attribute, changing the name of
+ // the parameter from i to baz. This requires Black Magic...
+ //
+ // The (well-designed) classfile library (correctly) does not
+ // allow us to mess around with the attribute data structures,
+ // or arbitrarily generate new ones.
+ //
+ // Instead, we install a new subclass of Attribute that
+ // hijacks the Visitor pattern and outputs the sequence of
+ // bytes that we want. This only works in this particular
+ // instance, because we know we'll only every see one kind of
+ // visitor.
+ //
+ // If anyone ever changes the makeup of the Baz class, or
+ // tries to install some kind of visitor that gets run prior
+ // to serialization, this will break.
+ baz.methods[0].attributes.attrs[mpind] =
+ new Attribute(mpattr.attribute_name_index,
+ mpattr.attribute_length) {
+ public <R, D> R accept(Visitor<R, D> visitor, D data) {
+ if (data instanceof ByteArrayOutputStream) {
+ ByteArrayOutputStream out =
+ (ByteArrayOutputStream) data;
+ out.write(1);
+ out.write((ind >> 8) & 0xff);
+ out.write(ind & 0xff);
+ out.write((flags >> 24) & 0xff);
+ out.write((flags >> 16) & 0xff);
+ out.write((flags >> 8) & 0xff);
+ out.write(flags & 0xff);
+ } else
+ throw new RuntimeException("Output stream is of type " + data.getClass() + ", which is not handled by this test. Update the test and it should work.");
+ return null;
+ }
+ };
+
+ // Flip the code and method attributes. This is for checking
+ // that order doesn't matter.
+ if (flip) {
+ baz.methods[0].attributes.attrs[mpind] = cattr;
+ baz.methods[0].attributes.attrs[cind] = mpattr;
+ }
+
+ new ClassWriter().write(baz, Baz_class);
+ }
+
+ // Run a bunch of structural tests on foo to make sure it looks right.
+ void checkFoo() throws Exception {
+ final File Foo_class = new File(classesdir, Foo_name + ".class");
+ final ClassFile foo = ClassFile.read(Foo_class);
+ for (int i = 0; i < foo.methods.length; i++) {
+ System.err.println("Examine method Foo." + foo.methods[i].getName(foo.constant_pool));
+ if (foo.methods[i].getName(foo.constant_pool).equals("foo2")) {
+ for (int j = 0; j < foo.methods[i].attributes.attrs.length; j++)
+ if (foo.methods[i].attributes.attrs[j] instanceof
+ MethodParameters_attribute) {
+ MethodParameters_attribute mp =
+ (MethodParameters_attribute)
+ foo.methods[i].attributes.attrs[j];
+ System.err.println("Foo.foo2 should have 2 parameters: j and k");
+ if (2 != mp.method_parameter_table_length)
+ error("expected 2 method parameter entries in foo2, got " +
+ mp.method_parameter_table_length);
+ else if (!foo.constant_pool.getUTF8Value(mp.method_parameter_table[0].name_index).equals("j"))
+ error("expected first parameter to foo2 to be \"j\", got \"" +
+ foo.constant_pool.getUTF8Value(mp.method_parameter_table[0].name_index) +
+ "\" instead");
+ else if (!foo.constant_pool.getUTF8Value(mp.method_parameter_table[1].name_index).equals("k"))
+ error("expected first parameter to foo2 to be \"k\", got \"" +
+ foo.constant_pool.getUTF8Value(mp.method_parameter_table[1].name_index) +
+ "\" instead");
+ }
+ }
+ else if (foo.methods[i].getName(foo.constant_pool).equals("<init>")) {
+ for (int j = 0; j < foo.methods[i].attributes.attrs.length; j++) {
+ if (foo.methods[i].attributes.attrs[j] instanceof
+ MethodParameters_attribute)
+ error("Zero-argument constructor shouldn't have MethodParameters");
+ }
+ }
+ else if (foo.methods[i].getName(foo.constant_pool).equals("foo0")) {
+ for (int j = 0; j < foo.methods[i].attributes.attrs.length; j++)
+ if (foo.methods[i].attributes.attrs[j] instanceof
+ MethodParameters_attribute)
+ error("Zero-argument method shouldn't have MethodParameters");
+ }
+ else
+ error("Unknown method " + foo.methods[i].getName(foo.constant_pool) + " showed up in class Foo");
+ }
+ }
+
+ // Run a bunch of structural tests on Bar to make sure it looks right.
+ void checkBar() throws Exception {
+ final File Bar_class = new File(classesdir, Bar_name + ".class");
+ final ClassFile bar = ClassFile.read(Bar_class);
+ for (int i = 0; i < bar.methods.length; i++) {
+ System.err.println("Examine method Bar." + bar.methods[i].getName(bar.constant_pool));
+ if (bar.methods[i].getName(bar.constant_pool).equals("<init>")) {
+ for (int j = 0; j < bar.methods[i].attributes.attrs.length; j++)
+ if (bar.methods[i].attributes.attrs[j] instanceof
+ MethodParameters_attribute) {
+ MethodParameters_attribute mp =
+ (MethodParameters_attribute)
+ bar.methods[i].attributes.attrs[j];
+ System.err.println("Bar constructor should have 1 parameter: i");
+ if (1 != mp.method_parameter_table_length)
+ error("expected 1 method parameter entries in constructor, got " +
+ mp.method_parameter_table_length);
+ else if (!bar.constant_pool.getUTF8Value(mp.method_parameter_table[0].name_index).equals("i"))
+ error("expected first parameter to foo2 to be \"i\", got \"" +
+ bar.constant_pool.getUTF8Value(mp.method_parameter_table[0].name_index) +
+ "\" instead");
+ }
+ }
+ else if (bar.methods[i].getName(bar.constant_pool).equals("foo")) {
+ for (int j = 0; j < bar.methods[i].attributes.attrs.length; j++) {
+ if (bar.methods[i].attributes.attrs[j] instanceof
+ MethodParameters_attribute)
+ error("Zero-argument constructor shouldn't have MethodParameters");
+ }
+ }
+ }
+ }
+
+ String compile(String... args) throws Exception {
+ System.err.println("compile: " + Arrays.asList(args));
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ int rc = com.sun.tools.javac.Main.compile(args, pw);
+ pw.close();
+ String out = sw.toString();
+ if (out.length() > 0)
+ System.err.println(out);
+ if (rc != 0)
+ error("compilation failed, rc=" + rc);
+ return out;
+ }
+
+ File writeFile(File dir, String path, String body) throws IOException {
+ File f = new File(dir, path);
+ f.getParentFile().mkdirs();
+ FileWriter out = new FileWriter(f);
+ out.write(body);
+ out.close();
+ return f;
+ }
+
+ void error(String msg) {
+ System.err.println("Error: " + msg);
+ errors++;
+ }
+
+ int errors;
+}