1 /*
   2  * Copyright (c) 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 8161013
  27  * @summary Verify that anonymous class binaries have the correct flags set
  28  * @modules jdk.jdeps/com.sun.tools.classfile
  29  * @run main AnonymousClassFlags
  30  */
  31 
  32 import java.util.*;
  33 import java.nio.file.Path;
  34 import java.nio.file.Paths;
  35 
  36 import com.sun.tools.classfile.*;
  37 import static com.sun.tools.classfile.AccessFlags.*;
  38 
  39 public class AnonymousClassFlags {
  40     public static void main(String[] args) throws Exception {
  41         new AnonymousClassFlags().test(System.getProperty("test.classes", "."));
  42     }
  43 
  44     /** Maps names of anonymous classes to their expected inner_class_access_flags */
  45     private static Map<String, Integer> anonClasses = new LinkedHashMap<>();
  46 
  47     // ******* TEST CASES ********
  48 
  49     static Object o1 = new Object() {
  50         { anonClasses.put(getClass().getName(), 0); }
  51     };
  52 
  53     static void staticMethod() {
  54         Object o2 = new Object() {
  55             { anonClasses.put(getClass().getName(), 0); }
  56         };
  57     }
  58 
  59     static {
  60         staticMethod();
  61 
  62         Object o3 = new Object() {
  63             { anonClasses.put(getClass().getName(), 0); }
  64         };
  65     }
  66 
  67     Object o4 = new Object() {
  68         { anonClasses.put(getClass().getName(), 0); }
  69     };
  70 
  71     void instanceMethod() {
  72         Object o5 = new Object() {
  73             { anonClasses.put(getClass().getName(), 0); }
  74         };
  75     }
  76 
  77     {
  78         instanceMethod();
  79 
  80         Object o6 = new Object() {
  81             { anonClasses.put(getClass().getName(), 0); }
  82         };
  83     }
  84 
  85     // ******* TEST IMPLEMENTATION ********
  86 
  87     void test(String classesDir) throws Exception {
  88         staticMethod();
  89         instanceMethod();
  90 
  91         Path outerFile = Paths.get(classesDir, getClass().getName() + ".class");
  92         ClassFile outerClass = ClassFile.read(outerFile);
  93         for (Map.Entry<String,Integer> entry : anonClasses.entrySet()) {
  94             Path innerFile = Paths.get(classesDir, entry.getKey() + ".class");
  95             ClassFile innerClass = ClassFile.read(innerFile);
  96             String name = entry.getKey();
  97             int expected = entry.getValue();
  98             assertInnerFlags(outerClass, name, expected);
  99             assertClassFlags(innerClass, name, expected);
 100             assertInnerFlags(innerClass, name, expected);
 101         }
 102     }
 103 
 104     static void assertClassFlags(ClassFile classFile, String name, int expected) {
 105         int mask = ACC_PUBLIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT |
 106                    ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM;
 107         int classExpected = (expected & mask) | ACC_SUPER;
 108         int classActual = classFile.access_flags.flags;
 109         if (classActual != classExpected) {
 110             throw new AssertionError("Incorrect access_flags for class " + name +
 111                                      ": expected=" + classExpected + ", actual=" + classActual);
 112         }
 113 
 114     }
 115 
 116     static void assertInnerFlags(ClassFile classFile, String name, int expected) throws ConstantPoolException {
 117         int innerActual = lookupInnerFlags(classFile, name).flags;
 118         if (innerActual != expected) {
 119             throw new AssertionError("Incorrect inner_class_access_flags for class " + name +
 120                                      " in class " + classFile.getName() +
 121                                      ": expected=" + expected + ", actual=" + innerActual);
 122         }
 123     }
 124 
 125     private static AccessFlags lookupInnerFlags(ClassFile classFile, String innerName) throws ConstantPoolException {
 126         InnerClasses_attribute inners = (InnerClasses_attribute) classFile.getAttribute("InnerClasses");
 127         if (inners == null) {
 128             throw new AssertionError("InnerClasses attribute missing in class " + classFile.getName());
 129         }
 130         for (InnerClasses_attribute.Info info : inners.classes) {
 131             String entryName = info.getInnerClassInfo(classFile.constant_pool).getName();
 132             if (innerName.equals(entryName)) {
 133                 return info.inner_class_access_flags;
 134             }
 135         }
 136         throw new AssertionError("No InnerClasses entry in class " + classFile.getName() + " for class " + innerName);
 137     }
 138 
 139 }