Sat, 29 Dec 2012 17:33:17 -0800
8004727: Add compiler support for parameter reflection
Reviewed-by: jjg
Contributed-by: eric.mccorkle@oracle.com
1.1 --- a/src/share/classes/com/sun/tools/classfile/Attribute.java Fri Dec 28 22:25:21 2012 -0800 1.2 +++ b/src/share/classes/com/sun/tools/classfile/Attribute.java Sat Dec 29 17:33:17 2012 -0800 1.3 @@ -1,5 +1,5 @@ 1.4 /* 1.5 - * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. 1.6 + * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved. 1.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.8 * 1.9 * This code is free software; you can redistribute it and/or modify it 1.10 @@ -51,6 +51,7 @@ 1.11 public static final String LineNumberTable = "LineNumberTable"; 1.12 public static final String LocalVariableTable = "LocalVariableTable"; 1.13 public static final String LocalVariableTypeTable = "LocalVariableTypeTable"; 1.14 + public static final String MethodParameters = "MethodParameters"; 1.15 public static final String RuntimeVisibleAnnotations = "RuntimeVisibleAnnotations"; 1.16 public static final String RuntimeInvisibleAnnotations = "RuntimeInvisibleAnnotations"; 1.17 public static final String RuntimeVisibleParameterAnnotations = "RuntimeVisibleParameterAnnotations"; 1.18 @@ -113,6 +114,7 @@ 1.19 standardAttributes.put(LocalVariableTypeTable, LocalVariableTypeTable_attribute.class); 1.20 1.21 if (!compat) { // old javap does not recognize recent attributes 1.22 + standardAttributes.put(MethodParameters, MethodParameters_attribute.class); 1.23 standardAttributes.put(CompilationID, CompilationID_attribute.class); 1.24 standardAttributes.put(RuntimeInvisibleAnnotations, RuntimeInvisibleAnnotations_attribute.class); 1.25 standardAttributes.put(RuntimeInvisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations_attribute.class); 1.26 @@ -171,6 +173,7 @@ 1.27 R visitLineNumberTable(LineNumberTable_attribute attr, P p); 1.28 R visitLocalVariableTable(LocalVariableTable_attribute attr, P p); 1.29 R visitLocalVariableTypeTable(LocalVariableTypeTable_attribute attr, P p); 1.30 + R visitMethodParameters(MethodParameters_attribute attr, P p); 1.31 R visitRuntimeVisibleAnnotations(RuntimeVisibleAnnotations_attribute attr, P p); 1.32 R visitRuntimeInvisibleAnnotations(RuntimeInvisibleAnnotations_attribute attr, P p); 1.33 R visitRuntimeVisibleParameterAnnotations(RuntimeVisibleParameterAnnotations_attribute attr, P p);
2.1 --- a/src/share/classes/com/sun/tools/classfile/ClassWriter.java Fri Dec 28 22:25:21 2012 -0800 2.2 +++ b/src/share/classes/com/sun/tools/classfile/ClassWriter.java Sat Dec 29 17:33:17 2012 -0800 2.3 @@ -1,6 +1,6 @@ 2.4 2.5 /* 2.6 - * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. 2.7 + * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved. 2.8 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 2.9 * 2.10 * This code is free software; you can redistribute it and/or modify it 2.11 @@ -479,6 +479,15 @@ 2.12 out.writeShort(entry.index); 2.13 } 2.14 2.15 + public Void visitMethodParameters(MethodParameters_attribute attr, ClassOutputStream out) { 2.16 + out.writeByte(attr.method_parameter_table.length); 2.17 + for (MethodParameters_attribute.Entry e : attr.method_parameter_table) { 2.18 + out.writeShort(e.name_index); 2.19 + out.writeInt(e.flags); 2.20 + } 2.21 + return null; 2.22 + } 2.23 + 2.24 public Void visitRuntimeVisibleAnnotations(RuntimeVisibleAnnotations_attribute attr, ClassOutputStream out) { 2.25 annotationWriter.write(attr.annotations, out); 2.26 return null;
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/src/share/classes/com/sun/tools/classfile/MethodParameters_attribute.java Sat Dec 29 17:33:17 2012 -0800 3.3 @@ -0,0 +1,87 @@ 3.4 +/* 3.5 + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. 3.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3.7 + * 3.8 + * This code is free software; you can redistribute it and/or modify it 3.9 + * under the terms of the GNU General Public License version 2 only, as 3.10 + * published by the Free Software Foundation. Oracle designates this 3.11 + * particular file as subject to the "Classpath" exception as provided 3.12 + * by Oracle in the LICENSE file that accompanied this code. 3.13 + * 3.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 3.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 3.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 3.17 + * version 2 for more details (a copy is included in the LICENSE file that 3.18 + * accompanied this code). 3.19 + * 3.20 + * You should have received a copy of the GNU General Public License version 3.21 + * 2 along with this work; if not, write to the Free Software Foundation, 3.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 3.23 + * 3.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 3.25 + * or visit www.oracle.com if you need additional information or have any 3.26 + * questions. 3.27 + */ 3.28 + 3.29 +package com.sun.tools.classfile; 3.30 + 3.31 +import java.io.IOException; 3.32 + 3.33 +/** 3.34 + * See JVMS, section 4.8.13. 3.35 + * 3.36 + * <p><b>This is NOT part of any supported API. 3.37 + * If you write code that depends on this, you do so at your own risk. 3.38 + * This code and its internal interfaces are subject to change or 3.39 + * deletion without notice.</b> 3.40 + */ 3.41 +public class MethodParameters_attribute extends Attribute { 3.42 + 3.43 + public final int method_parameter_table_length; 3.44 + public final Entry[] method_parameter_table; 3.45 + 3.46 + MethodParameters_attribute(ClassReader cr, 3.47 + int name_index, 3.48 + int length) 3.49 + throws IOException { 3.50 + super(name_index, length); 3.51 + 3.52 + method_parameter_table_length = cr.readUnsignedByte(); 3.53 + method_parameter_table = new Entry[method_parameter_table_length]; 3.54 + for (int i = 0; i < method_parameter_table_length; i++) 3.55 + method_parameter_table[i] = new Entry(cr); 3.56 + } 3.57 + 3.58 + public MethodParameters_attribute(ConstantPool constant_pool, 3.59 + Entry[] method_parameter_table) 3.60 + throws ConstantPoolException { 3.61 + this(constant_pool.getUTF8Index(Attribute.MethodParameters), 3.62 + method_parameter_table); 3.63 + } 3.64 + 3.65 + public MethodParameters_attribute(int name_index, 3.66 + Entry[] method_parameter_table) { 3.67 + super(name_index, 1 + method_parameter_table.length * Entry.length()); 3.68 + this.method_parameter_table_length = method_parameter_table.length; 3.69 + this.method_parameter_table = method_parameter_table; 3.70 + } 3.71 + 3.72 + public <R, D> R accept(Visitor<R, D> visitor, D data) { 3.73 + return visitor.visitMethodParameters(this, data); 3.74 + } 3.75 + 3.76 + public static class Entry { 3.77 + Entry(ClassReader cr) throws IOException { 3.78 + name_index = cr.readUnsignedShort(); 3.79 + flags = cr.readInt(); 3.80 + } 3.81 + 3.82 + public static int length() { 3.83 + return 6; 3.84 + } 3.85 + 3.86 + public final int name_index; 3.87 + public final int flags; 3.88 + } 3.89 + 3.90 +}
4.1 --- a/src/share/classes/com/sun/tools/javac/code/Symbol.java Fri Dec 28 22:25:21 2012 -0800 4.2 +++ b/src/share/classes/com/sun/tools/javac/code/Symbol.java Sat Dec 29 17:33:17 2012 -0800 4.3 @@ -1283,8 +1283,9 @@ 4.4 List<Name> paramNames = savedParameterNames; 4.5 savedParameterNames = null; 4.6 // discard the provided names if the list of names is the wrong size. 4.7 - if (paramNames == null || paramNames.size() != type.getParameterTypes().size()) 4.8 + if (paramNames == null || paramNames.size() != type.getParameterTypes().size()) { 4.9 paramNames = List.nil(); 4.10 + } 4.11 ListBuffer<VarSymbol> buf = new ListBuffer<VarSymbol>(); 4.12 List<Name> remaining = paramNames; 4.13 // assert: remaining and paramNames are both empty or both
5.1 --- a/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java Fri Dec 28 22:25:21 2012 -0800 5.2 +++ b/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java Sat Dec 29 17:33:17 2012 -0800 5.3 @@ -217,6 +217,13 @@ 5.4 */ 5.5 boolean haveParameterNameIndices; 5.6 5.7 + /** Set this to false every time we start reading a method 5.8 + * and are saving parameter names. Set it to true when we see 5.9 + * MethodParameters, if it's set when we see a LocalVariableTable, 5.10 + * then we ignore the parameter names from the LVT. 5.11 + */ 5.12 + boolean sawMethodParameters; 5.13 + 5.14 /** 5.15 * The set of attribute names for which warnings have been generated for the current class 5.16 */ 5.17 @@ -984,7 +991,7 @@ 5.18 new AttributeReader(names.LocalVariableTable, V45_3, CLASS_OR_MEMBER_ATTRIBUTE) { 5.19 protected void read(Symbol sym, int attrLen) { 5.20 int newbp = bp + attrLen; 5.21 - if (saveParameterNames) { 5.22 + if (saveParameterNames && !sawMethodParameters) { 5.23 // Pick up parameter names from the variable table. 5.24 // Parameter names are not explicitly identified as such, 5.25 // but all parameter name entries in the LocalVariableTable 5.26 @@ -1017,6 +1024,25 @@ 5.27 } 5.28 }, 5.29 5.30 + new AttributeReader(names.MethodParameters, V52, MEMBER_ATTRIBUTE) { 5.31 + protected void read(Symbol sym, int attrlen) { 5.32 + int newbp = bp + attrlen; 5.33 + if (saveParameterNames) { 5.34 + sawMethodParameters = true; 5.35 + int numEntries = nextByte(); 5.36 + parameterNameIndices = new int[numEntries]; 5.37 + haveParameterNameIndices = true; 5.38 + for (int i = 0; i < numEntries; i++) { 5.39 + int nameIndex = nextChar(); 5.40 + int flags = nextInt(); 5.41 + parameterNameIndices[i] = nameIndex; 5.42 + } 5.43 + } 5.44 + bp = newbp; 5.45 + } 5.46 + }, 5.47 + 5.48 + 5.49 new AttributeReader(names.SourceFile, V45_3, CLASS_ATTRIBUTE) { 5.50 protected void read(Symbol sym, int attrLen) { 5.51 ClassSymbol c = (ClassSymbol) sym; 5.52 @@ -1826,6 +1852,7 @@ 5.53 } else 5.54 Arrays.fill(parameterNameIndices, 0); 5.55 haveParameterNameIndices = false; 5.56 + sawMethodParameters = false; 5.57 } 5.58 5.59 /** 5.60 @@ -1845,12 +1872,16 @@ 5.61 // if no names were found in the class file, there's nothing more to do 5.62 if (!haveParameterNameIndices) 5.63 return; 5.64 - 5.65 - int firstParam = ((sym.flags() & STATIC) == 0) ? 1 : 0; 5.66 - // the code in readMethod may have skipped the first parameter when 5.67 - // setting up the MethodType. If so, we make a corresponding allowance 5.68 - // here for the position of the first parameter. Note that this 5.69 - // assumes the skipped parameter has a width of 1 -- i.e. it is not 5.70 + // If we get parameter names from MethodParameters, then we 5.71 + // don't need to skip. 5.72 + int firstParam = 0; 5.73 + if (!sawMethodParameters) { 5.74 + firstParam = ((sym.flags() & STATIC) == 0) ? 1 : 0; 5.75 + // the code in readMethod may have skipped the first 5.76 + // parameter when setting up the MethodType. If so, we 5.77 + // make a corresponding allowance here for the position of 5.78 + // the first parameter. Note that this assumes the 5.79 + // skipped parameter has a width of 1 -- i.e. it is not 5.80 // a double width type (long or double.) 5.81 if (sym.name == names.init && currentOwner.hasOuterInstance()) { 5.82 // Sometimes anonymous classes don't have an outer 5.83 @@ -1861,17 +1892,20 @@ 5.84 } 5.85 5.86 if (sym.type != jvmType) { 5.87 - // reading the method attributes has caused the symbol's type to 5.88 - // be changed. (i.e. the Signature attribute.) This may happen if 5.89 - // there are hidden (synthetic) parameters in the descriptor, but 5.90 - // not in the Signature. The position of these hidden parameters 5.91 - // is unspecified; for now, assume they are at the beginning, and 5.92 - // so skip over them. The primary case for this is two hidden 5.93 - // parameters passed into Enum constructors. 5.94 + // reading the method attributes has caused the 5.95 + // symbol's type to be changed. (i.e. the Signature 5.96 + // attribute.) This may happen if there are hidden 5.97 + // (synthetic) parameters in the descriptor, but not 5.98 + // in the Signature. The position of these hidden 5.99 + // parameters is unspecified; for now, assume they are 5.100 + // at the beginning, and so skip over them. The 5.101 + // primary case for this is two hidden parameters 5.102 + // passed into Enum constructors. 5.103 int skip = Code.width(jvmType.getParameterTypes()) 5.104 - Code.width(sym.type.getParameterTypes()); 5.105 firstParam += skip; 5.106 } 5.107 + } 5.108 List<Name> paramNames = List.nil(); 5.109 int index = firstParam; 5.110 for (Type t: sym.type.getParameterTypes()) {
6.1 --- a/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java Fri Dec 28 22:25:21 2012 -0800 6.2 +++ b/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java Sat Dec 29 17:33:17 2012 -0800 6.3 @@ -725,6 +725,28 @@ 6.4 return acount; 6.5 } 6.6 6.7 + /** 6.8 + * Write method parameter names attribute. 6.9 + */ 6.10 + int writeMethodParametersAttr(MethodSymbol m) { 6.11 + if (m.params != null && 0 != m.params.length()) { 6.12 + int attrIndex = writeAttr(names.MethodParameters); 6.13 + databuf.appendByte(m.params.length()); 6.14 + for (VarSymbol s : m.params) { 6.15 + // TODO: expand to cover synthesized, once we figure out 6.16 + // how to represent that. 6.17 + final int flags = (int) s.flags() & (FINAL | SYNTHETIC); 6.18 + // output parameter info 6.19 + databuf.appendChar(pool.put(s.name)); 6.20 + databuf.appendInt(flags); 6.21 + } 6.22 + endAttr(attrIndex); 6.23 + return 1; 6.24 + } else 6.25 + return 0; 6.26 + } 6.27 + 6.28 + 6.29 /** Write method parameter annotations; 6.30 * return number of attributes written. 6.31 */ 6.32 @@ -1034,6 +1056,8 @@ 6.33 endAttr(alenIdx); 6.34 acount++; 6.35 } 6.36 + if (options.isSet(PARAMETERS)) 6.37 + acount += writeMethodParametersAttr(m); 6.38 acount += writeMemberAttrs(m); 6.39 acount += writeParameterAttrs(m); 6.40 endAttrs(acountIdx, acount);
7.1 --- a/src/share/classes/com/sun/tools/javac/main/Option.java Fri Dec 28 22:25:21 2012 -0800 7.2 +++ b/src/share/classes/com/sun/tools/javac/main/Option.java Sat Dec 29 17:33:17 2012 -0800 7.3 @@ -176,6 +176,8 @@ 7.4 7.5 PROCESSORPATH("-processorpath", "opt.arg.path", "opt.processorpath", STANDARD, FILEMANAGER), 7.6 7.7 + PARAMETERS("-parameters","opt.parameters", STANDARD, BASIC), 7.8 + 7.9 D("-d", "opt.arg.directory", "opt.d", STANDARD, FILEMANAGER), 7.10 7.11 S("-s", "opt.arg.directory", "opt.sourceDest", STANDARD, FILEMANAGER),
8.1 --- a/src/share/classes/com/sun/tools/javac/resources/javac.properties Fri Dec 28 22:25:21 2012 -0800 8.2 +++ b/src/share/classes/com/sun/tools/javac/resources/javac.properties Sat Dec 29 17:33:17 2012 -0800 8.3 @@ -55,6 +55,8 @@ 8.4 Specify where to find annotation processors 8.5 javac.opt.processor=\ 8.6 Names of the annotation processors to run; bypasses default discovery process 8.7 +javac.opt.parameters=\ 8.8 + Generate metadata for reflection on method parameters 8.9 javac.opt.proc.none.only=\ 8.10 Control whether annotation processing and/or compilation is done. 8.11 javac.opt.d=\
9.1 --- a/src/share/classes/com/sun/tools/javac/util/Names.java Fri Dec 28 22:25:21 2012 -0800 9.2 +++ b/src/share/classes/com/sun/tools/javac/util/Names.java Sat Dec 29 17:33:17 2012 -0800 9.3 @@ -132,6 +132,7 @@ 9.4 public final Name LineNumberTable; 9.5 public final Name LocalVariableTable; 9.6 public final Name LocalVariableTypeTable; 9.7 + public final Name MethodParameters; 9.8 public final Name RuntimeInvisibleAnnotations; 9.9 public final Name RuntimeInvisibleParameterAnnotations; 9.10 public final Name RuntimeInvisibleTypeAnnotations; 9.11 @@ -265,6 +266,7 @@ 9.12 LineNumberTable = fromString("LineNumberTable"); 9.13 LocalVariableTable = fromString("LocalVariableTable"); 9.14 LocalVariableTypeTable = fromString("LocalVariableTypeTable"); 9.15 + MethodParameters = fromString("MethodParameters"); 9.16 RuntimeInvisibleAnnotations = fromString("RuntimeInvisibleAnnotations"); 9.17 RuntimeInvisibleParameterAnnotations = fromString("RuntimeInvisibleParameterAnnotations"); 9.18 RuntimeInvisibleTypeAnnotations = fromString("RuntimeInvisibleTypeAnnotations");
10.1 --- a/src/share/classes/com/sun/tools/javap/AttributeWriter.java Fri Dec 28 22:25:21 2012 -0800 10.2 +++ b/src/share/classes/com/sun/tools/javap/AttributeWriter.java Sat Dec 29 17:33:17 2012 -0800 10.3 @@ -1,5 +1,5 @@ 10.4 /* 10.5 - * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. 10.6 + * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved. 10.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 10.8 * 10.9 * This code is free software; you can redistribute it and/or modify it 10.10 @@ -46,6 +46,7 @@ 10.11 import com.sun.tools.classfile.LineNumberTable_attribute; 10.12 import com.sun.tools.classfile.LocalVariableTable_attribute; 10.13 import com.sun.tools.classfile.LocalVariableTypeTable_attribute; 10.14 +import com.sun.tools.classfile.MethodParameters_attribute; 10.15 import com.sun.tools.classfile.RuntimeInvisibleAnnotations_attribute; 10.16 import com.sun.tools.classfile.RuntimeInvisibleParameterAnnotations_attribute; 10.17 import com.sun.tools.classfile.RuntimeVisibleAnnotations_attribute; 10.18 @@ -386,6 +387,28 @@ 10.19 return null; 10.20 } 10.21 10.22 + private static final String format = "%-31s%s"; 10.23 + 10.24 + public Void visitMethodParameters(MethodParameters_attribute attr, 10.25 + Void ignore) { 10.26 + 10.27 + final String header = String.format(format, "Name", "Flags"); 10.28 + println("MethodParameters:"); 10.29 + indent(+1); 10.30 + println(header); 10.31 + for (MethodParameters_attribute.Entry entry : 10.32 + attr.method_parameter_table) { 10.33 + String flagstr = 10.34 + (0 != (entry.flags & ACC_FINAL) ? " final" : "") + 10.35 + (0 != (entry.flags & ACC_SYNTHETIC) ? " synthetic" : ""); 10.36 + println(String.format(format, 10.37 + constantWriter.stringValue(entry.name_index), 10.38 + flagstr)); 10.39 + } 10.40 + indent(-1); 10.41 + return null; 10.42 + } 10.43 + 10.44 public Void visitRuntimeVisibleAnnotations(RuntimeVisibleAnnotations_attribute attr, Void ignore) { 10.45 println("RuntimeVisibleAnnotations:"); 10.46 indent(+1);
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 11.2 +++ b/test/tools/javac/MethodParameters.java Sat Dec 29 17:33:17 2012 -0800 11.3 @@ -0,0 +1,343 @@ 11.4 +/* 11.5 + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. 11.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 11.7 + * 11.8 + * This code is free software; you can redistribute it and/or modify it 11.9 + * under the terms of the GNU General Public License version 2 only, as 11.10 + * published by the Free Software Foundation. 11.11 + * 11.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 11.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 11.15 + * version 2 for more details (a copy is included in the LICENSE file that 11.16 + * accompanied this code). 11.17 + * 11.18 + * You should have received a copy of the GNU General Public License version 11.19 + * 2 along with this work; if not, write to the Free Software Foundation, 11.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 11.21 + * 11.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 11.23 + * or visit www.oracle.com if you need additional information or have any 11.24 + * questions. 11.25 + */ 11.26 + 11.27 +/* 11.28 + * @test 11.29 + * @summary javac should generate method parameters correctly. 11.30 + */ 11.31 +// key: opt.arg.parameters 11.32 +import com.sun.tools.classfile.*; 11.33 +import com.sun.tools.javac.file.JavacFileManager; 11.34 +import com.sun.tools.javac.main.Main; 11.35 +import com.sun.tools.javac.util.Context; 11.36 +import com.sun.tools.javac.util.Name; 11.37 +import com.sun.tools.javac.util.Names; 11.38 +import java.io.*; 11.39 +import javax.lang.model.element.*; 11.40 +import java.util.*; 11.41 + 11.42 +public class MethodParameters { 11.43 + 11.44 + static final String Foo_name = "Foo"; 11.45 + static final String Foo_contents = 11.46 + "public class Foo {\n" + 11.47 + " Foo() {}\n" + 11.48 + " void foo0() {}\n" + 11.49 + " void foo2(int j, int k) {}\n" + 11.50 + "}"; 11.51 + static final String Bar_name = "Bar"; 11.52 + static final String Bar_contents = 11.53 + "public class Bar {\n" + 11.54 + " Bar(int i) {}" + 11.55 + " Foo foo() { return new Foo(); }\n" + 11.56 + "}"; 11.57 + static final String Baz_name = "Baz"; 11.58 + static final String Baz_contents = 11.59 + "public class Baz {\n" + 11.60 + " int baz;" + 11.61 + " Baz(int i) {}" + 11.62 + "}"; 11.63 + static final String Qux_name = "Qux"; 11.64 + static final String Qux_contents = 11.65 + "public class Qux extends Baz {\n" + 11.66 + " Qux(int i) { super(i); }" + 11.67 + "}"; 11.68 + static final File classesdir = new File("methodparameters"); 11.69 + 11.70 + public static void main(String... args) throws Exception { 11.71 + new MethodParameters().run(); 11.72 + } 11.73 + 11.74 + void run() throws Exception { 11.75 + classesdir.mkdir(); 11.76 + final File Foo_java = 11.77 + writeFile(classesdir, Foo_name + ".java", Foo_contents); 11.78 + final File Bar_java = 11.79 + writeFile(classesdir, Bar_name + ".java", Bar_contents); 11.80 + final File Baz_java = 11.81 + writeFile(classesdir, Baz_name + ".java", Baz_contents); 11.82 + System.err.println("Test compile with -parameter"); 11.83 + compile("-parameters", "-d", classesdir.getPath(), Foo_java.getPath()); 11.84 + // First test: make sure javac doesn't choke to death on 11.85 + // MethodParameter attributes 11.86 + System.err.println("Test compile with classfile containing MethodParameter attributes"); 11.87 + compile("-parameters", "-d", classesdir.getPath(), 11.88 + "-cp", classesdir.getPath(), Bar_java.getPath()); 11.89 + System.err.println("Examine class foo"); 11.90 + checkFoo(); 11.91 + checkBar(); 11.92 + System.err.println("Test debug information conflict"); 11.93 + compile("-g", "-parameters", "-d", classesdir.getPath(), 11.94 + "-cp", classesdir.getPath(), Baz_java.getPath()); 11.95 + System.err.println("Introducing debug information conflict"); 11.96 + Baz_java.delete(); 11.97 + modifyBaz(false); 11.98 + System.err.println("Checking language model"); 11.99 + inspectBaz(); 11.100 + System.err.println("Permuting attributes"); 11.101 + modifyBaz(true); 11.102 + System.err.println("Checking language model"); 11.103 + inspectBaz(); 11.104 + 11.105 + if(0 != errors) 11.106 + throw new Exception("MethodParameters test failed with " + 11.107 + errors + " errors"); 11.108 + } 11.109 + 11.110 + void inspectBaz() throws Exception { 11.111 + final File Qux_java = 11.112 + writeFile(classesdir, Qux_name + ".java", Qux_contents); 11.113 + final String[] args = { "-XDsave-parameter-names", "-d", 11.114 + classesdir.getPath(), 11.115 + "-cp", classesdir.getPath(), 11.116 + Qux_java.getPath() }; 11.117 + final StringWriter sw = new StringWriter(); 11.118 + final PrintWriter pw = new PrintWriter(sw); 11.119 + 11.120 + // We need to be able to crack open javac and look at its data 11.121 + // structures. We'll rig up a compiler instance, but keep its 11.122 + // Context, thus allowing us to get at the ClassReader. 11.123 + Context context = new Context(); 11.124 + Main comp = new Main("javac", pw); 11.125 + JavacFileManager.preRegister(context); 11.126 + 11.127 + // Compile Qux, which uses Baz. 11.128 + comp.compile(args, context); 11.129 + pw.close(); 11.130 + final String out = sw.toString(); 11.131 + if (out.length() > 0) 11.132 + System.err.println(out); 11.133 + 11.134 + // Now get the class reader, construct a name for Baz, and load it. 11.135 + com.sun.tools.javac.jvm.ClassReader cr = 11.136 + com.sun.tools.javac.jvm.ClassReader.instance(context); 11.137 + Name name = Names.instance(context).fromString(Baz_name); 11.138 + 11.139 + // Now walk down the language model and check the name of the 11.140 + // parameter. 11.141 + final Element baz = cr.loadClass(name); 11.142 + for (Element e : baz.getEnclosedElements()) { 11.143 + if (e instanceof ExecutableElement) { 11.144 + final ExecutableElement ee = (ExecutableElement) e; 11.145 + final List<? extends VariableElement> params = 11.146 + ee.getParameters(); 11.147 + if (1 != params.size()) 11.148 + throw new Exception("Classfile Baz badly formed: wrong number of methods"); 11.149 + final VariableElement param = params.get(0); 11.150 + if (!param.getSimpleName().contentEquals("baz")) { 11.151 + errors++; 11.152 + System.err.println("javac did not correctly resolve the metadata conflict, parameter's name reads as " + param.getSimpleName()); 11.153 + } else 11.154 + System.err.println("javac did correctly resolve the metadata conflict"); 11.155 + } 11.156 + } 11.157 + } 11.158 + 11.159 + void modifyBaz(boolean flip) throws Exception { 11.160 + final File Baz_class = new File(classesdir, Baz_name + ".class"); 11.161 + final ClassFile baz = ClassFile.read(Baz_class); 11.162 + final int ind = baz.constant_pool.getUTF8Index("baz"); 11.163 + MethodParameters_attribute mpattr = null; 11.164 + int mpind = 0; 11.165 + Code_attribute cattr = null; 11.166 + int cind = 0; 11.167 + 11.168 + // Find the indexes of the MethodParameters and the Code attributes 11.169 + if (baz.methods.length != 1) 11.170 + throw new Exception("Classfile Baz badly formed: wrong number of methods"); 11.171 + if (!baz.methods[0].getName(baz.constant_pool).equals("<init>")) 11.172 + throw new Exception("Classfile Baz badly formed: method has name " + 11.173 + baz.methods[0].getName(baz.constant_pool)); 11.174 + for (int i = 0; i < baz.methods[0].attributes.attrs.length; i++) { 11.175 + if (baz.methods[0].attributes.attrs[i] instanceof 11.176 + MethodParameters_attribute) { 11.177 + mpattr = (MethodParameters_attribute) 11.178 + baz.methods[0].attributes.attrs[i]; 11.179 + mpind = i; 11.180 + } else if (baz.methods[0].attributes.attrs[i] instanceof 11.181 + Code_attribute) { 11.182 + cattr = (Code_attribute) baz.methods[0].attributes.attrs[i]; 11.183 + cind = i; 11.184 + } 11.185 + } 11.186 + if (null == mpattr) 11.187 + throw new Exception("Classfile Baz badly formed: no method parameters info"); 11.188 + if (null == cattr) 11.189 + throw new Exception("Classfile Baz badly formed: no local variable table"); 11.190 + 11.191 + int flags = mpattr.method_parameter_table[0].flags; 11.192 + 11.193 + // Alter the MethodParameters attribute, changing the name of 11.194 + // the parameter from i to baz. This requires Black Magic... 11.195 + // 11.196 + // The (well-designed) classfile library (correctly) does not 11.197 + // allow us to mess around with the attribute data structures, 11.198 + // or arbitrarily generate new ones. 11.199 + // 11.200 + // Instead, we install a new subclass of Attribute that 11.201 + // hijacks the Visitor pattern and outputs the sequence of 11.202 + // bytes that we want. This only works in this particular 11.203 + // instance, because we know we'll only every see one kind of 11.204 + // visitor. 11.205 + // 11.206 + // If anyone ever changes the makeup of the Baz class, or 11.207 + // tries to install some kind of visitor that gets run prior 11.208 + // to serialization, this will break. 11.209 + baz.methods[0].attributes.attrs[mpind] = 11.210 + new Attribute(mpattr.attribute_name_index, 11.211 + mpattr.attribute_length) { 11.212 + public <R, D> R accept(Visitor<R, D> visitor, D data) { 11.213 + if (data instanceof ByteArrayOutputStream) { 11.214 + ByteArrayOutputStream out = 11.215 + (ByteArrayOutputStream) data; 11.216 + out.write(1); 11.217 + out.write((ind >> 8) & 0xff); 11.218 + out.write(ind & 0xff); 11.219 + out.write((flags >> 24) & 0xff); 11.220 + out.write((flags >> 16) & 0xff); 11.221 + out.write((flags >> 8) & 0xff); 11.222 + out.write(flags & 0xff); 11.223 + } else 11.224 + throw new RuntimeException("Output stream is of type " + data.getClass() + ", which is not handled by this test. Update the test and it should work."); 11.225 + return null; 11.226 + } 11.227 + }; 11.228 + 11.229 + // Flip the code and method attributes. This is for checking 11.230 + // that order doesn't matter. 11.231 + if (flip) { 11.232 + baz.methods[0].attributes.attrs[mpind] = cattr; 11.233 + baz.methods[0].attributes.attrs[cind] = mpattr; 11.234 + } 11.235 + 11.236 + new ClassWriter().write(baz, Baz_class); 11.237 + } 11.238 + 11.239 + // Run a bunch of structural tests on foo to make sure it looks right. 11.240 + void checkFoo() throws Exception { 11.241 + final File Foo_class = new File(classesdir, Foo_name + ".class"); 11.242 + final ClassFile foo = ClassFile.read(Foo_class); 11.243 + for (int i = 0; i < foo.methods.length; i++) { 11.244 + System.err.println("Examine method Foo." + foo.methods[i].getName(foo.constant_pool)); 11.245 + if (foo.methods[i].getName(foo.constant_pool).equals("foo2")) { 11.246 + for (int j = 0; j < foo.methods[i].attributes.attrs.length; j++) 11.247 + if (foo.methods[i].attributes.attrs[j] instanceof 11.248 + MethodParameters_attribute) { 11.249 + MethodParameters_attribute mp = 11.250 + (MethodParameters_attribute) 11.251 + foo.methods[i].attributes.attrs[j]; 11.252 + System.err.println("Foo.foo2 should have 2 parameters: j and k"); 11.253 + if (2 != mp.method_parameter_table_length) 11.254 + error("expected 2 method parameter entries in foo2, got " + 11.255 + mp.method_parameter_table_length); 11.256 + else if (!foo.constant_pool.getUTF8Value(mp.method_parameter_table[0].name_index).equals("j")) 11.257 + error("expected first parameter to foo2 to be \"j\", got \"" + 11.258 + foo.constant_pool.getUTF8Value(mp.method_parameter_table[0].name_index) + 11.259 + "\" instead"); 11.260 + else if (!foo.constant_pool.getUTF8Value(mp.method_parameter_table[1].name_index).equals("k")) 11.261 + error("expected first parameter to foo2 to be \"k\", got \"" + 11.262 + foo.constant_pool.getUTF8Value(mp.method_parameter_table[1].name_index) + 11.263 + "\" instead"); 11.264 + } 11.265 + } 11.266 + else if (foo.methods[i].getName(foo.constant_pool).equals("<init>")) { 11.267 + for (int j = 0; j < foo.methods[i].attributes.attrs.length; j++) { 11.268 + if (foo.methods[i].attributes.attrs[j] instanceof 11.269 + MethodParameters_attribute) 11.270 + error("Zero-argument constructor shouldn't have MethodParameters"); 11.271 + } 11.272 + } 11.273 + else if (foo.methods[i].getName(foo.constant_pool).equals("foo0")) { 11.274 + for (int j = 0; j < foo.methods[i].attributes.attrs.length; j++) 11.275 + if (foo.methods[i].attributes.attrs[j] instanceof 11.276 + MethodParameters_attribute) 11.277 + error("Zero-argument method shouldn't have MethodParameters"); 11.278 + } 11.279 + else 11.280 + error("Unknown method " + foo.methods[i].getName(foo.constant_pool) + " showed up in class Foo"); 11.281 + } 11.282 + } 11.283 + 11.284 + // Run a bunch of structural tests on Bar to make sure it looks right. 11.285 + void checkBar() throws Exception { 11.286 + final File Bar_class = new File(classesdir, Bar_name + ".class"); 11.287 + final ClassFile bar = ClassFile.read(Bar_class); 11.288 + for (int i = 0; i < bar.methods.length; i++) { 11.289 + System.err.println("Examine method Bar." + bar.methods[i].getName(bar.constant_pool)); 11.290 + if (bar.methods[i].getName(bar.constant_pool).equals("<init>")) { 11.291 + for (int j = 0; j < bar.methods[i].attributes.attrs.length; j++) 11.292 + if (bar.methods[i].attributes.attrs[j] instanceof 11.293 + MethodParameters_attribute) { 11.294 + MethodParameters_attribute mp = 11.295 + (MethodParameters_attribute) 11.296 + bar.methods[i].attributes.attrs[j]; 11.297 + System.err.println("Bar constructor should have 1 parameter: i"); 11.298 + if (1 != mp.method_parameter_table_length) 11.299 + error("expected 1 method parameter entries in constructor, got " + 11.300 + mp.method_parameter_table_length); 11.301 + else if (!bar.constant_pool.getUTF8Value(mp.method_parameter_table[0].name_index).equals("i")) 11.302 + error("expected first parameter to foo2 to be \"i\", got \"" + 11.303 + bar.constant_pool.getUTF8Value(mp.method_parameter_table[0].name_index) + 11.304 + "\" instead"); 11.305 + } 11.306 + } 11.307 + else if (bar.methods[i].getName(bar.constant_pool).equals("foo")) { 11.308 + for (int j = 0; j < bar.methods[i].attributes.attrs.length; j++) { 11.309 + if (bar.methods[i].attributes.attrs[j] instanceof 11.310 + MethodParameters_attribute) 11.311 + error("Zero-argument constructor shouldn't have MethodParameters"); 11.312 + } 11.313 + } 11.314 + } 11.315 + } 11.316 + 11.317 + String compile(String... args) throws Exception { 11.318 + System.err.println("compile: " + Arrays.asList(args)); 11.319 + StringWriter sw = new StringWriter(); 11.320 + PrintWriter pw = new PrintWriter(sw); 11.321 + int rc = com.sun.tools.javac.Main.compile(args, pw); 11.322 + pw.close(); 11.323 + String out = sw.toString(); 11.324 + if (out.length() > 0) 11.325 + System.err.println(out); 11.326 + if (rc != 0) 11.327 + error("compilation failed, rc=" + rc); 11.328 + return out; 11.329 + } 11.330 + 11.331 + File writeFile(File dir, String path, String body) throws IOException { 11.332 + File f = new File(dir, path); 11.333 + f.getParentFile().mkdirs(); 11.334 + FileWriter out = new FileWriter(f); 11.335 + out.write(body); 11.336 + out.close(); 11.337 + return f; 11.338 + } 11.339 + 11.340 + void error(String msg) { 11.341 + System.err.println("Error: " + msg); 11.342 + errors++; 11.343 + } 11.344 + 11.345 + int errors; 11.346 +}
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 12.2 +++ b/test/tools/javap/MethodParameters.java Sat Dec 29 17:33:17 2012 -0800 12.3 @@ -0,0 +1,165 @@ 12.4 +/* 12.5 + * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. 12.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 12.7 + * 12.8 + * This code is free software; you can redistribute it and/or modify it 12.9 + * under the terms of the GNU General Public License version 2 only, as 12.10 + * published by the Free Software Foundation. 12.11 + * 12.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 12.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12.15 + * version 2 for more details (a copy is included in the LICENSE file that 12.16 + * accompanied this code). 12.17 + * 12.18 + * You should have received a copy of the GNU General Public License version 12.19 + * 2 along with this work; if not, write to the Free Software Foundation, 12.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 12.21 + * 12.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 12.23 + * or visit www.oracle.com if you need additional information or have any 12.24 + * questions. 12.25 + */ 12.26 + 12.27 +/* 12.28 + * @test 12.29 + * @summary javac should generate method parameters correctly. 12.30 + */ 12.31 + 12.32 +import java.io.*; 12.33 +import java.util.*; 12.34 + 12.35 +public class MethodParameters { 12.36 + 12.37 + static final String Foo_name = "Foo"; 12.38 + static final String Foo_contents = 12.39 + ("public class Foo {\n" + 12.40 + " Foo() {}\n" + 12.41 + " Foo(int i) {}\n" + 12.42 + " void foo0() {}\n" + 12.43 + " void foo2(int j, int k) {}\n" + 12.44 + "}").replaceAll(" +", " "); 12.45 + static final String Init0_expected = 12.46 + (" Foo();\n" + 12.47 + " flags: \n" + 12.48 + " Code:\n" + 12.49 + " stack=1, locals=1, args_size=1\n" + 12.50 + " 0: aload_0 \n" + 12.51 + " 1: invokespecial #1 // Method java/lang/Object.\"<init>\":()V\n" + 12.52 + " 4: return \n" + 12.53 + " LineNumberTable:\n" + 12.54 + " line 2: 0").replaceAll(" +", " "); 12.55 + static final String Init1_expected = 12.56 + (" Foo(int);\n" + 12.57 + " flags: \n" + 12.58 + " Code:\n" + 12.59 + " stack=1, locals=2, args_size=2\n" + 12.60 + " 0: aload_0 \n" + 12.61 + " 1: invokespecial #1 // Method java/lang/Object.\"<init>\":()V\n" + 12.62 + " 4: return \n" + 12.63 + " LineNumberTable:\n" + 12.64 + " line 3: 0\n" + 12.65 + " MethodParameters:\n" + 12.66 + " Name Flags\n" + 12.67 + " i").replaceAll(" +", " "); 12.68 + static final String foo0_expected = 12.69 + (" void foo0();\n" + 12.70 + " flags: \n" + 12.71 + " Code:\n" + 12.72 + " stack=0, locals=1, args_size=1\n" + 12.73 + " 0: return \n" + 12.74 + " LineNumberTable:\n" + 12.75 + " line 4: 0").replaceAll(" +", " "); 12.76 + static final String foo2_expected = 12.77 + (" void foo2(int, int);\n" + 12.78 + " flags: \n" + 12.79 + " Code:\n" + 12.80 + " stack=0, locals=3, args_size=3\n" + 12.81 + " 0: return \n" + 12.82 + " LineNumberTable:\n" + 12.83 + " line 5: 0\n" + 12.84 + " MethodParameters:\n" + 12.85 + " Name Flags\n" + 12.86 + " j \n" + 12.87 + " k").replaceAll(" +", " "); 12.88 + 12.89 + static final File classesdir = new File("methodparameters"); 12.90 + static final String separator = System.getProperty("line.separator"); 12.91 + 12.92 + public static void main(String... args) throws Exception { 12.93 + new MethodParameters().run(); 12.94 + } 12.95 + 12.96 + void run() throws Exception { 12.97 + classesdir.mkdir(); 12.98 + final File Foo_java = 12.99 + writeFile(classesdir, Foo_name + ".java", Foo_contents); 12.100 + compile("-parameters", "-d", classesdir.getPath(), Foo_java.getPath()); 12.101 + System.out.println("Run javap"); 12.102 + String output = 12.103 + javap("-c", "-verbose", "-classpath", 12.104 + classesdir.getPath(), Foo_name); 12.105 + String normalized = 12.106 + output.replaceAll(separator, "\n").replaceAll(" +", " "); 12.107 + 12.108 + if (!normalized.contains(Init0_expected)) 12.109 + error("Bad output for zero-parameter constructor. Expected\n" + 12.110 + Init0_expected + "\n" + "but got\n" + normalized); 12.111 + if (!normalized.contains(Init1_expected)) 12.112 + error("Bad output for one-parameter constructor. Expected\n" + 12.113 + Init1_expected + "\n" + "but got\n" + normalized); 12.114 + if (!normalized.contains(foo0_expected)) 12.115 + error("Bad output for zero-parameter method. Expected\n" + 12.116 + foo0_expected + "\n" + "but got\n" + normalized); 12.117 + if (!normalized.contains(foo2_expected)) 12.118 + error("Bad output for two-parameter method Expected\n" + 12.119 + foo2_expected + "\n" + "but got\n" + normalized); 12.120 + 12.121 + if (0 != errors) 12.122 + throw new Exception("MethodParameters test failed with " + 12.123 + errors + " errors"); 12.124 + } 12.125 + 12.126 + String javap(String... args) { 12.127 + StringWriter sw = new StringWriter(); 12.128 + PrintWriter out = new PrintWriter(sw); 12.129 + //sun.tools.javap.Main.entry(args); 12.130 + int rc = com.sun.tools.javap.Main.run(args, out); 12.131 + if (rc != 0) 12.132 + throw new Error("javap failed. rc=" + rc); 12.133 + out.close(); 12.134 + System.out.println(sw); 12.135 + return sw.toString(); 12.136 + } 12.137 + 12.138 + String compile(String... args) throws Exception { 12.139 + System.err.println("compile: " + Arrays.asList(args)); 12.140 + StringWriter sw = new StringWriter(); 12.141 + PrintWriter pw = new PrintWriter(sw); 12.142 + int rc = com.sun.tools.javac.Main.compile(args, pw); 12.143 + pw.close(); 12.144 + String out = sw.toString(); 12.145 + if (out.length() > 0) 12.146 + System.err.println(out); 12.147 + if (rc != 0) 12.148 + error("compilation failed, rc=" + rc); 12.149 + return out; 12.150 + } 12.151 + 12.152 + File writeFile(File dir, String path, String body) throws IOException { 12.153 + File f = new File(dir, path); 12.154 + f.getParentFile().mkdirs(); 12.155 + FileWriter out = new FileWriter(f); 12.156 + out.write(body); 12.157 + out.close(); 12.158 + return f; 12.159 + } 12.160 + 12.161 + void error(String msg) { 12.162 + System.err.println("Error: " + msg); 12.163 + errors++; 12.164 + } 12.165 + 12.166 + int errors; 12.167 + 12.168 +}