ParameterNamesAreNotCopiedToAnonymousInitTest.java revision 2942:08092deced3f
1/* 2 * Copyright (c) 2013, 2015, 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 */ 23 24/* 25 * @test 26 * @bug 8010737 27 * @summary javac, known parameter's names should be copied to automatically 28 * generated constructors for inner classes 29 * @modules jdk.jdeps/com.sun.tools.classfile 30 * jdk.compiler/com.sun.tools.javac.api 31 * jdk.compiler/com.sun.tools.javac.code 32 * jdk.compiler/com.sun.tools.javac.tree 33 * jdk.compiler/com.sun.tools.javac.util 34 * @run main ParameterNamesAreNotCopiedToAnonymousInitTest check_class_file check_init_symbol 35 */ 36 37import java.io.File; 38import java.io.IOException; 39import java.lang.annotation.ElementType; 40import java.lang.annotation.Target; 41import java.nio.file.Paths; 42import java.util.Arrays; 43 44import javax.tools.JavaCompiler; 45import javax.tools.JavaFileObject; 46import javax.tools.StandardJavaFileManager; 47import javax.tools.ToolProvider; 48 49import com.sun.source.tree.CompilationUnitTree; 50import com.sun.source.tree.Tree; 51import com.sun.source.util.JavacTask; 52import com.sun.source.util.TaskEvent; 53import com.sun.source.util.TaskListener; 54import com.sun.tools.classfile.ClassFile; 55import com.sun.tools.classfile.Method; 56import com.sun.tools.javac.api.BasicJavacTask; 57import com.sun.tools.javac.code.Attribute.Compound; 58import com.sun.tools.javac.code.Symbol.ClassSymbol; 59import com.sun.tools.javac.code.Symbol.VarSymbol; 60import com.sun.tools.javac.tree.JCTree; 61import com.sun.tools.javac.tree.TreeScanner; 62import com.sun.tools.javac.util.Assert; 63import com.sun.tools.javac.util.Context; 64import com.sun.tools.javac.util.List; 65import com.sun.tools.javac.util.Names; 66 67public class ParameterNamesAreNotCopiedToAnonymousInitTest { 68 69 static final String noParamsErrorMsg = 70 "Test most be invoked with at least one parameter: check_class_file " + 71 "and/or check_init_symbol"; 72 static final String wrongParamsErrorMsg = 73 "Accepted arguments are: check_class_file and check_init_symbol"; 74 static final String paramNameNotCopiedAssertionMsg = 75 "The param name hasn't been copied to the init method"; 76 static final String noAnnotationsForParameterMsg = 77 "No annotations for seek parameter"; 78 static final String seekMethodNotFound = 79 "The seek init method was not found or conditions were not met"; 80 static final String nonNullParamPositionsMsg = 81 "Parameter positions shold not be null"; 82 static final String compilationFailed = 83 "Compilation failed"; 84 static final String seekMethodNotFoundMsg = 85 "The seek method was not found"; 86 87 static final String ParamAnnotationClassName = 88 ParameterNamesAreNotCopiedToAnonymousInitTest.class.getSimpleName() + "." + 89 ParamAnnotation.class.getSimpleName(); 90 91 public static void main(String[] args) throws Exception { 92 if (args.length == 0) { 93 throw new Error(noParamsErrorMsg); 94 } 95 new ParameterNamesAreNotCopiedToAnonymousInitTest().run(args); 96 } 97 98 void run(String[] args) throws Exception { 99 for (String arg : args) { 100 if (arg.equals("check_class_file")) { 101 checkClassFile(new File(Paths.get(System.getProperty("test.classes"), 102 this.getClass().getName() + "$initParams$1.class").toUri()), 1); 103 checkClassFile(new File(Paths.get(System.getProperty("test.classes"), 104 this.getClass().getName() + "$Generics$1.class").toUri()), 2); 105 } else if (arg.equals("check_init_symbol")) { 106 checkInitSymbol("m1", Arrays.asList(0), Arrays.asList("i")); 107 checkInitSymbol("m2", Arrays.asList(0, 1), Arrays.asList("t1", "t2")); 108 } else { 109 error(wrongParamsErrorMsg); 110 } 111 } 112 } 113 114 void checkClassFile(final File cfile, int numberOfParams) throws Exception { 115 ClassFile classFile = ClassFile.read(cfile); 116 boolean methodFound = false; 117 for (Method method : classFile.methods) { 118 if (method.getName(classFile.constant_pool).equals("<init>")) { 119 methodFound = true; 120 } 121 } 122 Assert.check(methodFound, seekMethodNotFoundMsg); 123 } 124 125 /* This method expect a non-null ordered list of integers, listing the 126 * position of the parameters to be checked on the init method. Position 0 127 * corresponds to the first parameter. 128 * 129 * As we are looking for a constructor of an anonymous class, the 130 * classOwnerName parameter must be the name of the method where the 131 * anonymous class is declared. 132 */ 133 void checkInitSymbol( 134 final String classOwnerName, 135 final java.util.List<Integer> paramsToCheck, 136 final java.util.List<String> paramNames) 137 throws IOException { 138 Assert.checkNonNull(paramsToCheck, nonNullParamPositionsMsg); 139 JavaCompiler c = ToolProvider.getSystemJavaCompiler(); 140 try (StandardJavaFileManager fm = c.getStandardFileManager(null, null, null)) { 141 Iterable<? extends JavaFileObject> fos = 142 fm.getJavaFileObjectsFromFiles( 143 Arrays.asList(new File(System.getProperty("test.src"), 144 this.getClass().getName() + ".java"))); 145 JavacTask task = (JavacTask) c.getTask(null, fm, null, 146 Arrays.asList("-d", System.getProperty("user.dir")), null, fos); 147 148 BasicJavacTask impl = (BasicJavacTask)task; 149 Context context = impl.getContext(); 150 final Names names = Names.instance(context); 151 152 task.addTaskListener(new TaskListener() { 153 154 @Override 155 public void started(TaskEvent e) {} 156 157 @Override 158 public void finished(TaskEvent e) { 159 class TheTreeScanner extends TreeScanner { 160 boolean foundAndCorrect = false; 161 162 @Override 163 public void visitMethodDef(JCTree.JCMethodDecl tree) { 164 ClassSymbol clazz = (ClassSymbol)tree.sym.owner; 165 if (clazz.owner.name.toString().equals(classOwnerName) && 166 tree.sym.name == names.init) { 167 168 int currentParamPos = 0; 169 int paramArrayIndex = 0; 170 171 List<VarSymbol> params = tree.sym.params; 172 while (params.nonEmpty() && paramArrayIndex < paramsToCheck.size()) { 173 VarSymbol param = params.head; 174 if (currentParamPos == paramsToCheck.get(paramArrayIndex)) { 175 if (!param.name.toString() 176 .equals(paramNames.get(paramArrayIndex))) { 177 error(paramNameNotCopiedAssertionMsg); 178 } 179 paramArrayIndex++; 180 } 181 currentParamPos++; 182 params = params.tail; 183 } 184 foundAndCorrect = paramArrayIndex >= paramsToCheck.size(); 185 } 186 super.visitMethodDef(tree); 187 } 188 } 189 190 if (e.getKind() == TaskEvent.Kind.ANALYZE) { 191 CompilationUnitTree compUnitTree = e.getCompilationUnit(); 192 boolean foundAndCorrect = false; 193 for (Tree tree : compUnitTree.getTypeDecls()) { 194 TheTreeScanner scanner = new TheTreeScanner(); 195 scanner.scan((JCTree) tree); 196 foundAndCorrect = foundAndCorrect | scanner.foundAndCorrect; 197 } 198 if (!foundAndCorrect) { 199 error(seekMethodNotFound); 200 } 201 } 202 } 203 }); 204 205 if (!task.call()) { 206 error(compilationFailed); 207 } 208 } 209 } 210 211 void error(String msg) { 212 throw new AssertionError(msg); 213 } 214 215 @Target(value = {ElementType.PARAMETER}) 216 @interface ParamAnnotation {} 217 218 /* If more cases are added in the future, it should be taken into account 219 * that method checkInitSymbol locates the inner class looking for its 220 * container method, which in the cases below are m1 and m2. So new cases 221 * must have different names for container methods or method checkInitSymbol 222 * should be changed. 223 */ 224 public class initParams { 225 public initParams(@ParamAnnotation int i) {} 226 227 public void m1() { 228 new initParams(2) {}; 229 } 230 } 231 232 class Generics<T1> { 233 T1 obj1; 234 Object obj2; 235 <T2> Generics(@ParamAnnotation T1 t1, @ParamAnnotation T2 t2) { 236 obj1 = t1; 237 obj2 = t2; 238 } 239 240 void m2() { 241 Generics<Integer> a = new <String>Generics<Integer>( 242 new Integer(11), "foo") {}; 243 } 244 } 245} 246