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