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