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