test/tools/javac/MethodParameters.java

Sat, 29 Dec 2012 17:33:17 -0800

author
jjg
date
Sat, 29 Dec 2012 17:33:17 -0800
changeset 1473
31780dd06ec7
child 1478
a9cb93cca229
permissions
-rw-r--r--

8004727: Add compiler support for parameter reflection
Reviewed-by: jjg
Contributed-by: eric.mccorkle@oracle.com

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

mercurial