test/tools/javac/MethodParametersTest.java

Wed, 13 Aug 2014 14:50:00 -0700

author
katleman
date
Wed, 13 Aug 2014 14:50:00 -0700
changeset 2549
0b6cc4ea670f
parent 1594
267225edc1fe
child 2525
2eb010b6cb22
permissions
-rw-r--r--

Added tag jdk8u40-b01 for changeset bf89a471779d

     1 /*
     2  * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.
     8  *
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    12  * version 2 for more details (a copy is included in the LICENSE file that
    13  * accompanied this code).
    14  *
    15  * You should have received a copy of the GNU General Public License version
    16  * 2 along with this work; if not, write to the Free Software Foundation,
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    18  *
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    20  * or visit www.oracle.com if you need additional information or have any
    21  * questions.
    22  */
    24 /*
    25  * @test
    26  * @bug 8004727
    27  * @summary javac should generate method parameters correctly.
    28  */
    29 // key: opt.arg.parameters
    30 import com.sun.tools.classfile.*;
    31 import com.sun.tools.javac.file.JavacFileManager;
    32 import com.sun.tools.javac.main.Main;
    33 import com.sun.tools.javac.util.Context;
    34 import com.sun.tools.javac.util.Name;
    35 import com.sun.tools.javac.util.Names;
    36 import java.io.*;
    37 import javax.lang.model.element.*;
    38 import java.util.*;
    40 public class MethodParametersTest {
    42     static final String Foo_name = "Foo";
    43     static final String Foo_contents =
    44         "public class Foo {\n" +
    45         "  Foo() {}\n" +
    46         "  void foo0() {}\n" +
    47         "  void foo2(int j, int k) {}\n" +
    48         "}";
    49     static final String Bar_name = "Bar";
    50     static final String Bar_contents =
    51         "public class Bar {\n" +
    52         "  Bar(int i) {}" +
    53         "  Foo foo() { return new Foo(); }\n" +
    54         "}";
    55     static final String Baz_name = "Baz";
    56     static final String Baz_contents =
    57         "public class Baz {\n" +
    58         "  int baz;" +
    59         "  Baz(int i) {}" +
    60         "}";
    61     static final String Qux_name = "Qux";
    62     static final String Qux_contents =
    63         "public class Qux extends Baz {\n" +
    64         "  Qux(int i) { super(i); }" +
    65         "}";
    66     static final File classesdir = new File("methodparameters");
    68     public static void main(String... args) throws Exception {
    69         new MethodParametersTest().run();
    70     }
    72     void run() throws Exception {
    73         classesdir.mkdir();
    74         final File Foo_java =
    75             writeFile(classesdir, Foo_name + ".java", Foo_contents);
    76         final File Bar_java =
    77             writeFile(classesdir, Bar_name + ".java", Bar_contents);
    78         final File Baz_java =
    79             writeFile(classesdir, Baz_name + ".java", Baz_contents);
    80         System.err.println("Test compile with -parameter");
    81         compile("-parameters", "-d", classesdir.getPath(), Foo_java.getPath());
    82         // First test: make sure javac doesn't choke to death on
    83         // MethodParameter attributes
    84         System.err.println("Test compile with classfile containing MethodParameter attributes");
    85         compile("-parameters", "-d", classesdir.getPath(),
    86                 "-cp", classesdir.getPath(), Bar_java.getPath());
    87         System.err.println("Examine class foo");
    88         checkFoo();
    89         checkBar();
    90         System.err.println("Test debug information conflict");
    91         compile("-g", "-parameters", "-d", classesdir.getPath(),
    92                 "-cp", classesdir.getPath(), Baz_java.getPath());
    93         System.err.println("Introducing debug information conflict");
    94         Baz_java.delete();
    95         modifyBaz(false);
    96         System.err.println("Checking language model");
    97         inspectBaz();
    98         System.err.println("Permuting attributes");
    99         modifyBaz(true);
   100         System.err.println("Checking language model");
   101         inspectBaz();
   103         if(0 != errors)
   104             throw new Exception("MethodParameters test failed with " +
   105                                 errors + " errors");
   106     }
   108     void inspectBaz() throws Exception {
   109         final File Qux_java =
   110             writeFile(classesdir, Qux_name + ".java", Qux_contents);
   111         final String[] args = { "-XDsave-parameter-names", "-d",
   112                                 classesdir.getPath(),
   113                                 "-cp", classesdir.getPath(),
   114                                 Qux_java.getPath() };
   115         final StringWriter sw = new StringWriter();
   116         final PrintWriter pw = new PrintWriter(sw);
   118         // We need to be able to crack open javac and look at its data
   119         // structures.  We'll rig up a compiler instance, but keep its
   120         // Context, thus allowing us to get at the ClassReader.
   121         Context context = new Context();
   122         Main comp =  new Main("javac", pw);
   123         JavacFileManager.preRegister(context);
   125         // Compile Qux, which uses Baz.
   126         comp.compile(args, context);
   127         pw.close();
   128         final String out = sw.toString();
   129         if (out.length() > 0)
   130             System.err.println(out);
   132         // Now get the class reader, construct a name for Baz, and load it.
   133         com.sun.tools.javac.jvm.ClassReader cr =
   134             com.sun.tools.javac.jvm.ClassReader.instance(context);
   135         Name name = Names.instance(context).fromString(Baz_name);
   137         // Now walk down the language model and check the name of the
   138         // parameter.
   139         final Element baz = cr.loadClass(name);
   140         for (Element e : baz.getEnclosedElements()) {
   141             if (e instanceof ExecutableElement) {
   142                 final ExecutableElement ee = (ExecutableElement) e;
   143                 final List<? extends VariableElement> params =
   144                     ee.getParameters();
   145                 if (1 != params.size())
   146                     throw new Exception("Classfile Baz badly formed: wrong number of methods");
   147                 final VariableElement param = params.get(0);
   148                 if (!param.getSimpleName().contentEquals("baz")) {
   149                     errors++;
   150                     System.err.println("javac did not correctly resolve the metadata conflict, parameter's name reads as " + param.getSimpleName());
   151                 } else
   152                     System.err.println("javac did correctly resolve the metadata conflict");
   153             }
   154         }
   155     }
   157     void modifyBaz(boolean flip) throws Exception {
   158         final File Baz_class = new File(classesdir, Baz_name + ".class");
   159         final ClassFile baz = ClassFile.read(Baz_class);
   160         final int ind = baz.constant_pool.getUTF8Index("baz");
   161         MethodParameters_attribute mpattr = null;
   162         int mpind = 0;
   163         Code_attribute cattr = null;
   164         int cind = 0;
   166         // Find the indexes of the MethodParameters and the Code attributes
   167         if (baz.methods.length != 1)
   168             throw new Exception("Classfile Baz badly formed: wrong number of methods");
   169         if (!baz.methods[0].getName(baz.constant_pool).equals("<init>"))
   170             throw new Exception("Classfile Baz badly formed: method has name " +
   171                                 baz.methods[0].getName(baz.constant_pool));
   172         for (int i = 0; i < baz.methods[0].attributes.attrs.length; i++) {
   173             if (baz.methods[0].attributes.attrs[i] instanceof
   174                 MethodParameters_attribute) {
   175                 mpattr = (MethodParameters_attribute)
   176                     baz.methods[0].attributes.attrs[i];
   177                 mpind = i;
   178             } else if (baz.methods[0].attributes.attrs[i] instanceof
   179                        Code_attribute) {
   180                 cattr = (Code_attribute) baz.methods[0].attributes.attrs[i];
   181                 cind = i;
   182             }
   183         }
   184         if (null == mpattr)
   185             throw new Exception("Classfile Baz badly formed: no method parameters info");
   186         if (null == cattr)
   187             throw new Exception("Classfile Baz badly formed: no local variable table");
   189         int flags = mpattr.method_parameter_table[0].flags;
   191         // Alter the MethodParameters attribute, changing the name of
   192         // the parameter from i to baz.  This requires Black Magic...
   193         //
   194         // The (well-designed) classfile library (correctly) does not
   195         // allow us to mess around with the attribute data structures,
   196         // or arbitrarily generate new ones.
   197         //
   198         // Instead, we install a new subclass of Attribute that
   199         // hijacks the Visitor pattern and outputs the sequence of
   200         // bytes that we want.  This only works in this particular
   201         // instance, because we know we'll only every see one kind of
   202         // visitor.
   203         //
   204         // If anyone ever changes the makeup of the Baz class, or
   205         // tries to install some kind of visitor that gets run prior
   206         // to serialization, this will break.
   207         baz.methods[0].attributes.attrs[mpind] =
   208             new Attribute(mpattr.attribute_name_index,
   209                           mpattr.attribute_length) {
   210                 public <R, D> R accept(Visitor<R, D> visitor, D data) {
   211                     if (data instanceof ByteArrayOutputStream) {
   212                         ByteArrayOutputStream out =
   213                             (ByteArrayOutputStream) data;
   214                         out.write(1);
   215                         out.write((ind >> 8) & 0xff);
   216                         out.write(ind & 0xff);
   217                         out.write((flags >> 24) & 0xff);
   218                         out.write((flags >> 16) & 0xff);
   219                         out.write((flags >> 8) & 0xff);
   220                         out.write(flags & 0xff);
   221                     } else
   222                         throw new RuntimeException("Output stream is of type " + data.getClass() + ", which is not handled by this test.  Update the test and it should work.");
   223                     return null;
   224                 }
   225             };
   227         // Flip the code and method attributes.  This is for checking
   228         // that order doesn't matter.
   229         if (flip) {
   230             baz.methods[0].attributes.attrs[mpind] = cattr;
   231             baz.methods[0].attributes.attrs[cind] = mpattr;
   232         }
   234         new ClassWriter().write(baz, Baz_class);
   235     }
   237     // Run a bunch of structural tests on foo to make sure it looks right.
   238     void checkFoo() throws Exception {
   239         final File Foo_class = new File(classesdir, Foo_name + ".class");
   240         final ClassFile foo = ClassFile.read(Foo_class);
   241         for (int i = 0; i < foo.methods.length; i++) {
   242             System.err.println("Examine method Foo." + foo.methods[i].getName(foo.constant_pool));
   243             if (foo.methods[i].getName(foo.constant_pool).equals("foo2")) {
   244                 for (int j = 0; j < foo.methods[i].attributes.attrs.length; j++)
   245                     if (foo.methods[i].attributes.attrs[j] instanceof
   246                         MethodParameters_attribute) {
   247                         MethodParameters_attribute mp =
   248                             (MethodParameters_attribute)
   249                             foo.methods[i].attributes.attrs[j];
   250                         System.err.println("Foo.foo2 should have 2 parameters: j and k");
   251                         if (2 != mp.method_parameter_table_length)
   252                             error("expected 2 method parameter entries in foo2, got " +
   253                                   mp.method_parameter_table_length);
   254                         else if (!foo.constant_pool.getUTF8Value(mp.method_parameter_table[0].name_index).equals("j"))
   255                             error("expected first parameter to foo2 to be \"j\", got \"" +
   256                                   foo.constant_pool.getUTF8Value(mp.method_parameter_table[0].name_index) +
   257                                   "\" instead");
   258                         else if  (!foo.constant_pool.getUTF8Value(mp.method_parameter_table[1].name_index).equals("k"))
   259                             error("expected first parameter to foo2 to be \"k\", got \"" +
   260                                   foo.constant_pool.getUTF8Value(mp.method_parameter_table[1].name_index) +
   261                                   "\" instead");
   262                     }
   263             }
   264             else if (foo.methods[i].getName(foo.constant_pool).equals("<init>")) {
   265                 for (int j = 0; j < foo.methods[i].attributes.attrs.length; j++) {
   266                     if (foo.methods[i].attributes.attrs[j] instanceof
   267                         MethodParameters_attribute)
   268                         error("Zero-argument constructor shouldn't have MethodParameters");
   269                 }
   270             }
   271             else if (foo.methods[i].getName(foo.constant_pool).equals("foo0")) {
   272                 for (int j = 0; j < foo.methods[i].attributes.attrs.length; j++)
   273                     if (foo.methods[i].attributes.attrs[j] instanceof
   274                         MethodParameters_attribute)
   275                         error("Zero-argument method shouldn't have MethodParameters");
   276             }
   277             else
   278                 error("Unknown method " + foo.methods[i].getName(foo.constant_pool) + " showed up in class Foo");
   279         }
   280     }
   282     // Run a bunch of structural tests on Bar to make sure it looks right.
   283     void checkBar() throws Exception {
   284         final File Bar_class = new File(classesdir, Bar_name + ".class");
   285         final ClassFile bar = ClassFile.read(Bar_class);
   286         for (int i = 0; i < bar.methods.length; i++) {
   287             System.err.println("Examine method Bar." + bar.methods[i].getName(bar.constant_pool));
   288             if (bar.methods[i].getName(bar.constant_pool).equals("<init>")) {
   289                 for (int j = 0; j < bar.methods[i].attributes.attrs.length; j++)
   290                     if (bar.methods[i].attributes.attrs[j] instanceof
   291                         MethodParameters_attribute) {
   292                         MethodParameters_attribute mp =
   293                             (MethodParameters_attribute)
   294                             bar.methods[i].attributes.attrs[j];
   295                         System.err.println("Bar constructor should have 1 parameter: i");
   296                         if (1 != mp.method_parameter_table_length)
   297                             error("expected 1 method parameter entries in constructor, got " +
   298                                   mp.method_parameter_table_length);
   299                         else if (!bar.constant_pool.getUTF8Value(mp.method_parameter_table[0].name_index).equals("i"))
   300                             error("expected first parameter to foo2 to be \"i\", got \"" +
   301                                   bar.constant_pool.getUTF8Value(mp.method_parameter_table[0].name_index) +
   302                                   "\" instead");
   303                     }
   304             }
   305             else if (bar.methods[i].getName(bar.constant_pool).equals("foo")) {
   306                 for (int j = 0; j < bar.methods[i].attributes.attrs.length; j++) {
   307                     if (bar.methods[i].attributes.attrs[j] instanceof
   308                         MethodParameters_attribute)
   309                         error("Zero-argument constructor shouldn't have MethodParameters");
   310                 }
   311             }
   312         }
   313     }
   315     String compile(String... args) throws Exception {
   316         System.err.println("compile: " + Arrays.asList(args));
   317         StringWriter sw = new StringWriter();
   318         PrintWriter pw = new PrintWriter(sw);
   319         int rc = com.sun.tools.javac.Main.compile(args, pw);
   320         pw.close();
   321         String out = sw.toString();
   322         if (out.length() > 0)
   323             System.err.println(out);
   324         if (rc != 0)
   325             error("compilation failed, rc=" + rc);
   326         return out;
   327     }
   329     File writeFile(File dir, String path, String body) throws IOException {
   330         File f = new File(dir, path);
   331         f.getParentFile().mkdirs();
   332         FileWriter out = new FileWriter(f);
   333         out.write(body);
   334         out.close();
   335         return f;
   336     }
   338     void error(String msg) {
   339         System.err.println("Error: " + msg);
   340         errors++;
   341     }
   343     int errors;
   344 }

mercurial