1 /* 2 * Copyright (c) 2013, 2016, 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 37 import java.io.File; 38 import java.io.IOException; 39 import java.lang.annotation.ElementType; 40 import java.lang.annotation.Target; 41 import java.nio.file.Paths; 42 import java.util.Arrays; 43 44 import javax.tools.JavaCompiler; 45 import javax.tools.JavaFileObject; 46 import javax.tools.StandardJavaFileManager; 47 import javax.tools.ToolProvider; 48 49 import com.sun.source.tree.CompilationUnitTree; 50 import com.sun.source.tree.Tree; 51 import com.sun.source.util.JavacTask; 52 import com.sun.source.util.TaskEvent; 53 import com.sun.source.util.TaskListener; 54 import com.sun.tools.classfile.ClassFile; 55 import com.sun.tools.classfile.Method; 56 import com.sun.tools.javac.api.BasicJavacTask; 57 import com.sun.tools.javac.code.Attribute.Compound; 58 import com.sun.tools.javac.code.Symbol.ClassSymbol; 59 import com.sun.tools.javac.code.Symbol.VarSymbol; 60 import com.sun.tools.javac.tree.JCTree; 61 import com.sun.tools.javac.tree.TreeScanner; 62 import com.sun.tools.javac.util.Assert; 63 import com.sun.tools.javac.util.Context; 64 import com.sun.tools.javac.util.List; 65 import com.sun.tools.javac.util.Names; 66 67 public 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 java.util.List<String> options = Arrays.asList( 146 "--add-exports", "jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED", 147 "--add-exports", "jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", 148 "--add-exports", "jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", 149 "--add-exports", "jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", 150 "--add-exports", "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", 151 "-d", System.getProperty("user.dir") 152 ); 153 JavacTask task = (JavacTask) c.getTask(null, fm, null, options, null, fos); 154 155 BasicJavacTask impl = (BasicJavacTask)task; 156 Context context = impl.getContext(); 157 final Names names = Names.instance(context); 158 159 task.addTaskListener(new TaskListener() { 160 161 @Override 162 public void started(TaskEvent e) {} 163 164 @Override 165 public void finished(TaskEvent e) { 166 class TheTreeScanner extends TreeScanner { 167 boolean foundAndCorrect = false; 168 169 @Override 170 public void visitMethodDef(JCTree.JCMethodDecl tree) { 171 ClassSymbol clazz = (ClassSymbol)tree.sym.owner; 172 if (clazz.owner.name.toString().equals(classOwnerName) && 173 tree.sym.name == names.init) { 174 175 int currentParamPos = 0; 176 int paramArrayIndex = 0; 177 178 List<VarSymbol> params = tree.sym.params; 179 while (params.nonEmpty() && paramArrayIndex < paramsToCheck.size()) { 180 VarSymbol param = params.head; 181 if (currentParamPos == paramsToCheck.get(paramArrayIndex)) { 182 if (!param.name.toString() 183 .equals(paramNames.get(paramArrayIndex))) { 184 error(paramNameNotCopiedAssertionMsg); 185 } 186 paramArrayIndex++; 187 } 188 currentParamPos++; 189 params = params.tail; 190 } 191 foundAndCorrect = paramArrayIndex >= paramsToCheck.size(); 192 } 193 super.visitMethodDef(tree); 194 } 195 } 196 197 if (e.getKind() == TaskEvent.Kind.ANALYZE) { 198 CompilationUnitTree compUnitTree = e.getCompilationUnit(); 199 boolean foundAndCorrect = false; 200 for (Tree tree : compUnitTree.getTypeDecls()) { 201 TheTreeScanner scanner = new TheTreeScanner(); 202 scanner.scan((JCTree) tree); 203 foundAndCorrect = foundAndCorrect | scanner.foundAndCorrect; 204 } 205 if (!foundAndCorrect) { 206 error(seekMethodNotFound); 207 } 208 } 209 } 210 }); 211 212 if (!task.call()) { 213 error(compilationFailed); 214 } 215 } 216 } 217 218 void error(String msg) { 219 throw new AssertionError(msg); 220 } 221 222 @Target(value = {ElementType.PARAMETER}) 223 @interface ParamAnnotation {} 224 225 /* If more cases are added in the future, it should be taken into account 226 * that method checkInitSymbol locates the inner class looking for its 227 * container method, which in the cases below are m1 and m2. So new cases 228 * must have different names for container methods or method checkInitSymbol 229 * should be changed. 230 */ 231 public class initParams { 232 public initParams(@ParamAnnotation int i) {} 233 234 public void m1() { 235 new initParams(2) {}; 236 } 237 } 238 239 class Generics<T1> { 240 T1 obj1; 241 Object obj2; 242 <T2> Generics(@ParamAnnotation T1 t1, @ParamAnnotation T2 t2) { 243 obj1 = t1; 244 obj2 = t2; 245 } 246 247 void m2() { 248 Generics<Integer> a = new <String>Generics<Integer>( 249 new Integer(11), "foo") {}; 250 } 251 } 252 }