1 /* 2 * Copyright (c) 2014, 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 8042931 27 * @summary Checking EnclosingMethod attribute of anonymous/local class. 28 * @library /tools/lib /tools/javac/lib ../lib 29 * @modules jdk.jdeps/com.sun.tools.classfile 30 * jdk.compiler/com.sun.tools.javac.api 31 * jdk.compiler/com.sun.tools.javac.file 32 * jdk.compiler/com.sun.tools.javac.main 33 * @build EnclosingMethodTest TestBase TestResult InMemoryFileManager ToolBox 34 * @run main EnclosingMethodTest 35 */ 36 37 import com.sun.tools.classfile.Attribute; 38 import com.sun.tools.classfile.ClassFile; 39 import com.sun.tools.classfile.EnclosingMethod_attribute; 40 41 import java.io.File; 42 import java.io.FilenameFilter; 43 import java.lang.annotation.Retention; 44 import java.lang.annotation.RetentionPolicy; 45 import java.util.HashMap; 46 import java.util.HashSet; 47 import java.util.Map; 48 import java.util.Set; 49 import java.util.stream.Stream; 50 51 /** 52 * The test checks the enclosing method attribute of anonymous/local classes. 53 * The top-level class contains the anonymous and local classes to be tested. The test examines 54 * each inner class and determine whether the class should have the EnclosingMethod attribute or not. 55 * Golden information about enclosing methods are held in annotation {@code ExpectedEnclosingMethod}. 56 * 57 * The test assumes that a class must have the EnclosingMethod attribute if the class is annotated or 58 * if its parent class is annotated in case of anonymous class. In addition, classes 59 * named {@code VariableInitializer} are introduced to test variable initializer cases. These classes 60 * must not have the enclosing method attribute, but its anonymous derived class must. 61 * After classification of classes, the test checks whether classes contain the correct enclosing 62 * method attribute in case of anonymous/local class, or checks whether classes do not contain 63 * the EnclosingMethod attribute, otherwise. 64 * 65 * Test cases: 66 * top-level class as enclosing class: 67 * 1. anonymous and local classes in static initializer; 68 * 2. anonymous and local classes in instance initializer; 69 * 3. anonymous and local classes in lambda; 70 * 4. anonymous and local classes in constructor; 71 * 5. anonymous and local classes in method; 72 * 6. static and instance variable initializer. 73 * 74 * inner class as enclosing class: 75 * 1. anonymous and local classes in static initializer; 76 * 2. anonymous and local classes in instance initializer; 77 * 3. anonymous and local classes in lambda; 78 * 4. anonymous and local classes in constructor; 79 * 5. anonymous and local classes in method; 80 * 6. static and instance variable initializer. 81 * 82 * enum as enclosing class: 83 * 1. anonymous and local classes in static initializer; 84 * 2. anonymous and local classes in instance initializer; 85 * 3. anonymous and local classes in lambda; 86 * 4. anonymous and local classes in constructor; 87 * 5. anonymous and local classes in method; 88 * 6. static and instance variable initializer. 89 * 90 * interface as enclosing class: 91 * 1. anonymous and local classes in lambda; 92 * 2. anonymous and local classes in static method; 93 * 3. anonymous and local classes in default method; 94 * 4. static variable initializer. 95 * 96 * annotation as enclosing class: 97 * 1. anonymous and local classes in lambda; 98 * 2. static variable initializer. 99 */ 100 public class EnclosingMethodTest extends TestResult { 101 102 private final Map<Class<?>, ExpectedEnclosingMethod> class2EnclosingMethod = new HashMap<>(); 103 private final Set<Class<?>> noEnclosingMethod = new HashSet<>(); 104 105 public EnclosingMethodTest() throws ClassNotFoundException { 106 Class<EnclosingMethodTest> outerClass = EnclosingMethodTest.class; 107 String outerClassName = outerClass.getSimpleName(); 108 File testClasses = getClassDir(); 109 FilenameFilter filter = (dir, name) -> name.matches(outerClassName + ".*\\.class"); 110 111 for (File file : testClasses.listFiles(filter)) { 112 Class<?> clazz = Class.forName(file.getName().replace(".class", "")); 113 if (clazz.isAnonymousClass()) { 114 // anonymous class cannot be annotated, information is in its parent class. 115 ExpectedEnclosingMethod declaredAnnotation = 116 clazz.getSuperclass().getDeclaredAnnotation(ExpectedEnclosingMethod.class); 117 class2EnclosingMethod.put(clazz, declaredAnnotation); 118 } else { 119 ExpectedEnclosingMethod enclosingMethod = clazz.getDeclaredAnnotation(ExpectedEnclosingMethod.class); 120 // if class is annotated and it does not contain information for variable initializer cases, 121 // then it must have the enclosing method attribute. 122 if (enclosingMethod != null && !clazz.getSimpleName().contains("VariableInitializer")) { 123 class2EnclosingMethod.put(clazz, enclosingMethod); 124 } else { 125 noEnclosingMethod.add(clazz); 126 } 127 } 128 } 129 } 130 131 public void test() throws TestFailedException { 132 try { 133 testEnclosingMethodAttribute(); 134 testLackOfEnclosingMethodAttribute(); 135 } finally { 136 checkStatus(); 137 } 138 } 139 140 private void testLackOfEnclosingMethodAttribute() { 141 for (Class<?> clazz : noEnclosingMethod) { 142 try { 143 addTestCase("Class should not have EnclosingMethod attribute : " + clazz); 144 ClassFile classFile = readClassFile(clazz); 145 checkEquals(countEnclosingMethodAttributes(classFile), 146 0l, "number of the EnclosingMethod attribute in the class is zero : " 147 + classFile.getName()); 148 } catch (Exception e) { 149 addFailure(e); 150 } 151 } 152 } 153 154 private void testEnclosingMethodAttribute() { 155 class2EnclosingMethod.forEach((clazz, enclosingMethod) -> { 156 try { 157 String info = enclosingMethod.info() + " " 158 + (clazz.isAnonymousClass() ? "anonymous" : "local"); 159 addTestCase(info); 160 printf("Testing test case : %s\n", info); 161 ClassFile classFile = readClassFile(clazz); 162 String className = clazz.getName(); 163 checkEquals(countEnclosingMethodAttributes(classFile), 1l, 164 "number of the EnclosingMethod attribute in the class is one : " 165 + clazz); 166 EnclosingMethod_attribute attr = (EnclosingMethod_attribute) 167 classFile.getAttribute(Attribute.EnclosingMethod); 168 169 if (!checkNotNull(attr, "the EnclosingMethod attribute is not null : " + className)) { 170 // stop checking, attr is null. test case failed 171 return; 172 } 173 checkEquals(classFile.constant_pool.getUTF8Value(attr.attribute_name_index), 174 "EnclosingMethod", 175 "attribute_name_index of EnclosingMethod attribute in the class : " + className); 176 checkEquals(attr.attribute_length, 4, 177 "attribute_length of EnclosingMethod attribute in the class : " + className); 178 String expectedClassName = enclosingMethod.enclosingClazz().getName(); 179 checkEquals(classFile.constant_pool.getClassInfo(attr.class_index).getName(), 180 expectedClassName, String.format( 181 "enclosing class of EnclosingMethod attribute in the class %s is %s", 182 className, expectedClassName)); 183 184 String expectedMethodName = enclosingMethod.enclosingMethod(); 185 if (expectedMethodName.isEmpty()) { 186 // class does not have an enclosing method 187 checkEquals(attr.method_index, 0, String.format( 188 "enclosing method of EnclosingMethod attribute in the class %s is null", className)); 189 } else { 190 String methodName = classFile.constant_pool.getNameAndTypeInfo(attr.method_index).getName(); 191 checkTrue(methodName.startsWith(expectedMethodName), String.format( 192 "enclosing method of EnclosingMethod attribute in the class %s" + 193 " is method name %s" + 194 ", actual method name is %s", 195 className, expectedMethodName, methodName)); 196 } 197 } catch (Exception e) { 198 addFailure(e); 199 } 200 }); 201 } 202 203 private long countEnclosingMethodAttributes(ClassFile classFile) { 204 return Stream.of(classFile.attributes.attrs) 205 .filter(x -> x instanceof EnclosingMethod_attribute) 206 .count(); 207 } 208 209 @Retention(RetentionPolicy.RUNTIME) 210 public @interface ExpectedEnclosingMethod { 211 String info(); 212 Class<?> enclosingClazz(); 213 String enclosingMethod() default ""; 214 } 215 216 public static void main(String[] args) throws ClassNotFoundException, TestFailedException { 217 new EnclosingMethodTest().test(); 218 } 219 220 // Test cases: enclosing class is a top-level class 221 static { 222 // anonymous and local classes in static initializer 223 @ExpectedEnclosingMethod( 224 info = "EnclosingStaticInitialization in EnclosingMethodTest", 225 enclosingClazz = EnclosingMethodTest.class 226 ) 227 class EnclosingStaticInitialization { 228 } 229 new EnclosingStaticInitialization() { 230 }; 231 } 232 233 { 234 // anonymous and local classes in instance initializer 235 @ExpectedEnclosingMethod( 236 info = "EnclosingInitialization in EnclosingMethodTest", 237 enclosingClazz = EnclosingMethodTest.class 238 ) 239 class EnclosingInitialization { 240 } 241 new EnclosingInitialization() { 242 }; 243 } 244 245 Runnable lambda = () -> { 246 // anonymous and local classes in lambda 247 @ExpectedEnclosingMethod( 248 info = "EnclosingLambda in EnclosingMethodTest", 249 enclosingMethod = "lambda", 250 enclosingClazz = EnclosingMethodTest.class 251 ) 252 class EnclosingLambda { 253 } 254 new EnclosingLambda() { 255 }; 256 }; 257 258 EnclosingMethodTest(int i) { 259 // anonymous and local classes in constructor 260 @ExpectedEnclosingMethod( 261 info = "EnclosingConstructor in EnclosingMethodTest", 262 enclosingMethod = "<init>", 263 enclosingClazz = EnclosingMethodTest.class 264 ) 265 class EnclosingConstructor { 266 } 267 new EnclosingConstructor() { 268 }; 269 } 270 271 void method() { 272 // anonymous and local classes in method 273 @ExpectedEnclosingMethod( 274 info = "EnclosingMethod in EnclosingMethodTest", 275 enclosingMethod = "method", 276 enclosingClazz = EnclosingMethodTest.class 277 ) 278 class EnclosingMethod { 279 } 280 new EnclosingMethod() { 281 }; 282 } 283 284 @ExpectedEnclosingMethod( 285 info = "VariableInitializer in EnclosingMethodTest", 286 enclosingClazz = EnclosingMethodTest.class 287 ) 288 static class VariableInitializer { 289 } 290 291 // static variable initializer 292 private static final VariableInitializer cvi = new VariableInitializer() { 293 }; 294 295 // instance variable initializer 296 private final VariableInitializer ivi = new VariableInitializer() { 297 }; 298 299 // Test cases: enclosing class is an inner class 300 public static class notEnclosing01 { 301 static { 302 // anonymous and local classes in static initializer 303 @ExpectedEnclosingMethod( 304 info = "EnclosingStaticInitialization in notEnclosing01", 305 enclosingClazz = notEnclosing01.class 306 ) 307 class EnclosingStaticInitialization { 308 } 309 new EnclosingStaticInitialization() { 310 }; 311 } 312 313 { 314 // anonymous and local classes in instance initializer 315 @ExpectedEnclosingMethod( 316 info = "EnclosingInitialization in notEnclosing01", 317 enclosingClazz = notEnclosing01.class 318 ) 319 class EnclosingInitialization { 320 } 321 new EnclosingInitialization() { 322 }; 323 } 324 325 Runnable lambda = () -> { 326 // anonymous and local classes in lambda 327 @ExpectedEnclosingMethod( 328 info = "EnclosingLambda in notEnclosing01", 329 enclosingMethod = "lambda", 330 enclosingClazz = notEnclosing01.class 331 ) 332 class EnclosingLambda { 333 } 334 new EnclosingLambda() { 335 }; 336 }; 337 338 notEnclosing01() { 339 // anonymous and local classes in constructor 340 @ExpectedEnclosingMethod( 341 info = "EnclosingConstructor in notEnclosing01", 342 enclosingMethod = "<init>", 343 enclosingClazz = notEnclosing01.class 344 ) 345 class EnclosingConstructor { 346 } 347 new EnclosingConstructor() { 348 }; 349 } 350 351 void method() { 352 // anonymous and local classes in method 353 @ExpectedEnclosingMethod( 354 info = "EnclosingMethod in notEnclosing01", 355 enclosingMethod = "method", 356 enclosingClazz = notEnclosing01.class 357 ) 358 class EnclosingMethod { 359 } 360 new EnclosingMethod() { 361 }; 362 } 363 364 @ExpectedEnclosingMethod( 365 info = "VariableInitializer in notEnclosing01", 366 enclosingClazz = notEnclosing01.class 367 ) 368 static class VariableInitializer { 369 } 370 371 // static variable initializer 372 private static final VariableInitializer cvi = new VariableInitializer() { 373 }; 374 375 // instance variable initializer 376 private final VariableInitializer ivi = new VariableInitializer() { 377 }; 378 } 379 380 // Test cases: enclosing class is an interface 381 public interface notEnclosing02 { 382 Runnable lambda = () -> { 383 // anonymous and local classes in lambda 384 @ExpectedEnclosingMethod( 385 info = "EnclosingLambda in notEnclosing02", 386 enclosingMethod = "lambda", 387 enclosingClazz = notEnclosing02.class 388 ) 389 class EnclosingLambda { 390 } 391 new EnclosingLambda() { 392 }; 393 }; 394 395 static void staticMethod() { 396 // anonymous and local classes in static method 397 @ExpectedEnclosingMethod( 398 info = "EnclosingMethod in notEnclosing02", 399 enclosingMethod = "staticMethod", 400 enclosingClazz = notEnclosing02.class 401 ) 402 class EnclosingMethod { 403 } 404 new EnclosingMethod() { 405 }; 406 } 407 408 default void defaultMethod() { 409 // anonymous and local classes in default method 410 @ExpectedEnclosingMethod( 411 info = "EnclosingMethod in notEnclosing02", 412 enclosingMethod = "defaultMethod", 413 enclosingClazz = notEnclosing02.class 414 ) 415 class EnclosingMethod { 416 } 417 new EnclosingMethod() { 418 }; 419 } 420 421 @ExpectedEnclosingMethod( 422 info = "VariableInitializer in notEnclosing02", 423 enclosingClazz = notEnclosing02.class 424 ) 425 static class VariableInitializer { 426 } 427 428 // static variable initializer 429 VariableInitializer cvi = new VariableInitializer() { 430 }; 431 } 432 433 // Test cases: enclosing class is an enum 434 public enum notEnclosing03 {; 435 436 static { 437 // anonymous and local classes in static initializer 438 @ExpectedEnclosingMethod( 439 info = "EnclosingStaticInitialization in notEnclosing03", 440 enclosingClazz = notEnclosing03.class 441 ) 442 class EnclosingStaticInitialization { 443 } 444 new EnclosingStaticInitialization() { 445 }; 446 } 447 448 { 449 // anonymous and local classes in instance initializer 450 @ExpectedEnclosingMethod( 451 info = "EnclosingInitialization in notEnclosing03", 452 enclosingClazz = notEnclosing03.class 453 ) 454 class EnclosingInitialization { 455 } 456 new EnclosingInitialization() { 457 }; 458 } 459 460 Runnable lambda = () -> { 461 // anonymous and local classes in lambda 462 @ExpectedEnclosingMethod( 463 info = "EnclosingLambda in notEnclosing03", 464 enclosingMethod = "lambda", 465 enclosingClazz = notEnclosing03.class 466 ) 467 class EnclosingLambda { 468 } 469 new EnclosingLambda() { 470 }; 471 }; 472 473 notEnclosing03() { 474 // anonymous and local classes in constructor 475 @ExpectedEnclosingMethod( 476 info = "EnclosingConstructor in notEnclosing03", 477 enclosingMethod = "<init>", 478 enclosingClazz = notEnclosing03.class 479 ) 480 class EnclosingConstructor { 481 } 482 new EnclosingConstructor() { 483 }; 484 } 485 486 void method() { 487 // anonymous and local classes in method 488 @ExpectedEnclosingMethod( 489 info = "EnclosingMethod in notEnclosing03", 490 enclosingMethod = "method", 491 enclosingClazz = notEnclosing03.class 492 ) 493 class EnclosingMethod { 494 } 495 new EnclosingMethod() { 496 }; 497 } 498 499 @ExpectedEnclosingMethod( 500 info = "VariableInitializer in notEnclosing03", 501 enclosingClazz = notEnclosing03.class 502 ) 503 static class VariableInitializer { 504 } 505 506 // static variable initializer 507 private static final VariableInitializer cvi = new VariableInitializer() { 508 }; 509 510 // instance variable initializer 511 private final VariableInitializer ivi = new VariableInitializer() { 512 }; 513 } 514 515 // Test cases: enclosing class is an annotation 516 public @interface notEnclosing04 { 517 Runnable lambda = () -> { 518 // anonymous and local classes in lambda 519 @ExpectedEnclosingMethod( 520 info = "EnclosingLambda in notEnclosing04", 521 enclosingMethod = "lambda", 522 enclosingClazz = notEnclosing04.class 523 ) 524 class EnclosingLambda { 525 } 526 new EnclosingLambda() { 527 }; 528 }; 529 530 @ExpectedEnclosingMethod( 531 info = "VariableInitializer in notEnclosing04", 532 enclosingClazz = notEnclosing04.class 533 ) 534 static class VariableInitializer { 535 } 536 537 // static variable initializer 538 VariableInitializer cvi = new VariableInitializer() { 539 }; 540 } 541 }