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