test/tools/javac/MethodParametersTest.java

Wed, 14 Nov 2018 10:18:25 -0800

author
diazhou
date
Wed, 14 Nov 2018 10:18:25 -0800
changeset 3762
7909abb85562
parent 1594
267225edc1fe
child 2525
2eb010b6cb22
permissions
-rw-r--r--

Added tag jdk8u201-b04 for changeset a7f48b9dfb82

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

mercurial