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