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