1 /*
  2  * Copyright (c) 2014, 2024, 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 8042251
 27  * @summary Test that outer_class_info_index of local and anonymous class is zero.
 28  * @library /tools/lib /tools/javac/lib ../lib
 29  * @enablePreview
 30  * @modules jdk.compiler/com.sun.tools.javac.api
 31  *          jdk.compiler/com.sun.tools.javac.main
 32  * @build toolbox.ToolBox InMemoryFileManager TestResult TestBase
 33  * @run main InnerClassesIndexTest
 34  */
 35 
 36 import java.lang.classfile.*;
 37 import java.lang.classfile.attribute.*;
 38 import java.lang.classfile.constantpool.ClassEntry;
 39 import java.io.File;
 40 import java.io.FilenameFilter;
 41 import java.util.Arrays;
 42 import java.util.HashSet;
 43 import java.util.Objects;
 44 import java.util.Set;
 45 import java.util.stream.Collectors;
 46 
 47 
 48 public class InnerClassesIndexTest extends TestResult {
 49 
 50     public static void main(String[] args) throws TestFailedException {
 51         new InnerClassesIndexTest().test();
 52     }
 53 
 54     private boolean isExcluded(String className) {
 55         return !className.startsWith(InnerClassesIndexTest.class.getName())
 56                 || "InnerClassesIndexTest$Inner".equals(className);
 57     }
 58 
 59     private Set<String> getInnerClasses() {
 60         FilenameFilter filter = (dir, name) -> name.matches("InnerClassesIndexTest\\$.*\\.class");
 61         return Arrays.stream(Objects.requireNonNull(getClassDir().listFiles(filter)))
 62                 .map(File::getName)
 63                 .map(s -> s.replace(".class", ""))
 64                 .collect(Collectors.toSet());
 65     }
 66 
 67     public void test() throws TestFailedException {
 68         try {
 69             addTestCase("Source is InnerClassesIndexTest.java");
 70             ClassModel classFile = readClassFile(InnerClassesIndexTest.class);
 71             InnerClassesAttribute attr = classFile.findAttribute(Attributes.innerClasses()).orElse(null);
 72 
 73             Set<String> foundClasses = new HashSet<>();
 74             assert attr != null;
 75             for (InnerClassInfo info : attr.classes()) {
 76                 String innerName = info.innerClass().asInternalName();
 77                 echo("Testing class : " + innerName);
 78                 if (isExcluded(innerName)) {
 79                     echo("Ignored : " + innerName);
 80                     continue;
 81                 }
 82                 foundClasses.add(innerName);
 83                 ClassEntry out = info.outerClass().orElse(null);
 84                 checkEquals(out == null? 0: out.index(), 0,
 85                         "outer_class_info_index of " + innerName);
 86                 if (innerName.matches("\\$\\d+")) {
 87                     checkEquals(Objects.requireNonNull(info.innerName().orElse(null)).index(), 0,
 88                             "inner_name_index of anonymous class");
 89                 }
 90             }
 91             Set<String> expectedClasses = getInnerClasses();
 92             expectedClasses.remove("InnerClassesIndexTest$Inner");
 93             checkEquals(foundClasses, expectedClasses, "All classes are found");
 94         } catch (Exception e) {
 95             addFailure(e);
 96         } finally {
 97             checkStatus();
 98         }
 99     }
100 
101     static class Inner {
102     }
103 
104     Inner inner1 = new Inner() {
105     };
106 
107     static Inner inner2 = new Inner() {
108     };
109 
110     Runnable r = () -> {
111         class Local {
112         }
113         new Local() {
114         };
115     };
116 
117     public void local() {
118         class Local {
119         }
120         new Local() {
121         };
122     }
123 
124     public InnerClassesIndexTest() {
125         class Local {
126         }
127         new Local() {
128         };
129     }
130 
131     {
132         class Local {
133         }
134         new Local() {
135         };
136     }
137 
138     static {
139         class Local {
140         }
141         new Local() {
142         };
143     }
144 }