1 /*
2  * Copyright (c) 2014, 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 8042251
27  * @summary Test that inner classes have in its inner classes attribute enclosing classes and its immediate members.
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  *          jdk.jdeps/com.sun.tools.classfile
32  * @build toolbox.ToolBox InMemoryFileManager TestResult TestBase
33  * @run main InnerClassesHierarchyTest
34  */
35 
36 import java.io.File;
37 import java.io.FilenameFilter;
38 import java.io.IOException;
39 import java.lang.annotation.Annotation;
40 import java.util.*;
41 import java.util.stream.Collectors;
42 
43 import com.sun.tools.classfile.*;
44 import com.sun.tools.classfile.InnerClasses_attribute.Info;
45 
46 public class InnerClassesHierarchyTest extends TestResult {
47 
48     private final Map<String, Set<String>> innerClasses;
49     private final String outerClassName;
50 
InnerClassesHierarchyTest()51     public InnerClassesHierarchyTest() throws IOException, ConstantPoolException {
52         innerClasses = new HashMap<>();
53         outerClassName = InnerClassesHierarchyTest.class.getSimpleName();
54         File classDir = getClassDir();
55         FilenameFilter filter =
56                 (dir, name) -> name.matches(outerClassName + ".*\\.class");
57         for (File file : Arrays.asList(classDir.listFiles(filter))) {
58             ClassFile classFile = readClassFile(file);
59             String className = classFile.getName();
60             for (ConstantPool.CPInfo info : classFile.constant_pool.entries()) {
61                 if (info instanceof ConstantPool.CONSTANT_Class_info) {
62                     ConstantPool.CONSTANT_Class_info classInfo =
63                             (ConstantPool.CONSTANT_Class_info) info;
64                     String cpClassName = classInfo.getBaseName();
65                     if (isInnerClass(cpClassName)) {
66                         get(className).add(cpClassName);
67                     }
68                 }
69             }
70         }
71     }
72 
isInnerClass(String cpClassName)73     private boolean isInnerClass(String cpClassName) {
74         return cpClassName.contains("$");
75     }
76 
get(String className)77     private Set<String> get(String className) {
78         if (!innerClasses.containsKey(className)) {
79             innerClasses.put(className, new HashSet<>());
80         }
81         return innerClasses.get(className);
82     }
83 
main(String[] args)84     public static void main(String[] args) throws IOException, ConstantPoolException, TestFailedException {
85         new InnerClassesHierarchyTest().test();
86     }
87 
test()88     private void test() throws TestFailedException {
89         addTestCase("Source file is InnerClassesHierarchyTest.java");
90         try {
91             Queue<String> queue = new LinkedList<>();
92             Set<String> visitedClasses = new HashSet<>();
93             queue.add(outerClassName);
94             while (!queue.isEmpty()) {
95                 String currentClassName = queue.poll();
96                 if (!currentClassName.startsWith(outerClassName)) {
97                     continue;
98                 }
99                 ClassFile cf = readClassFile(currentClassName);
100                 InnerClasses_attribute attr = (InnerClasses_attribute)
101                         cf.getAttribute(Attribute.InnerClasses);
102                 checkNotNull(attr, "Class should not contain "
103                         + "inner classes attribute : " + currentClassName);
104                 checkTrue(innerClasses.containsKey(currentClassName),
105                         "map contains class name : " + currentClassName);
106                 Set<String> setClasses = innerClasses.get(currentClassName);
107                 if (setClasses == null) {
108                     continue;
109                 }
110                 checkEquals(attr.number_of_classes,
111                         setClasses.size(),
112                         "Check number of inner classes : " + setClasses);
113                 for (Info info : attr.classes) {
114                     String innerClassName = info
115                             .getInnerClassInfo(cf.constant_pool).getBaseName();
116                     checkTrue(setClasses.contains(innerClassName),
117                             currentClassName + " contains inner class : "
118                                     + innerClassName);
119                     if (visitedClasses.add(innerClassName)) {
120                         queue.add(innerClassName);
121                     }
122                 }
123             }
124             Set<String> allClasses = innerClasses.entrySet().stream()
125                     .flatMap(entry -> entry.getValue().stream())
126                     .collect(Collectors.toSet());
127 
128             Set<String> a_b = removeAll(visitedClasses, allClasses);
129             Set<String> b_a = removeAll(allClasses, visitedClasses);
130             checkEquals(visitedClasses, allClasses,
131                     "All classes are found\n"
132                             + "visited - all classes : " + a_b
133                             + "\nall classes - visited : " + b_a);
134         } catch (Exception e) {
135             addFailure(e);
136         } finally {
137             checkStatus();
138         }
139     }
140 
removeAll(Set<String> set1, Set<String> set2)141     private Set<String> removeAll(Set<String> set1, Set<String> set2) {
142         Set<String> set = new HashSet<>(set1);
143         set.removeAll(set2);
144         return set;
145     }
146 
147     public static class A1 {
148 
149         public class B1 {
150         }
151 
152         public enum B2 {
153         }
154 
155         public interface B3 {
156         }
157 
158         public @interface B4 {
159         }
160 
f()161         public void f() {
162             new B1() {
163             };
164             new B3() {
165             };
166             new B4() {
167                 @Override
168                 public Class<? extends Annotation> annotationType() {
169                     return null;
170                 }
171             };
172             class B5 {
173             }
174         }
175 
176         Runnable r = () -> {
177             new B1() {
178             };
179             new B3() {
180             };
181             new B4() {
182                 @Override
183                 public Class<? extends Annotation> annotationType() {
184                     return null;
185                 }
186             };
187             class B5 {
188             }
189         };
190     }
191 
192     public enum A2 {;
193 
194         public class B1 {
195         }
196 
197         public enum B2 {
198         }
199 
200         public interface B3 {
201         }
202 
203         public @interface B4 {
204         }
205 
a2()206         public void a2() {
207             new B1() {
208             };
209             new B3() {
210             };
211             new B4() {
212                 @Override
213                 public Class<? extends Annotation> annotationType() {
214                     return null;
215                 }
216             };
217             class B5 {
218             }
219         }
220 
221         Runnable r = () -> {
222             new B1() {
223             };
224             new B3() {
225             };
226             new B4() {
227                 @Override
228                 public Class<? extends Annotation> annotationType() {
229                     return null;
230                 }
231             };
232             class B5 {
233             }
234         };
235     }
236 
237     public interface A3 {
238 
239         public class B1 {
240         }
241 
242         public enum B2 {
243         }
244 
245         public interface B3 {
246         }
247 
248         public @interface B4 {
249         }
250 
a1()251         default void a1() {
252             new B1() {
253             };
254             new B3() {
255             };
256             new B4() {
257                 @Override
258                 public Class<? extends Annotation> annotationType() {
259                     return null;
260                 }
261             };
262             class B5 {
263             }
264         }
265 
a2()266         static void a2() {
267             new B1() {
268             };
269             new B3() {
270             };
271             new B4() {
272                 @Override
273                 public Class<? extends Annotation> annotationType() {
274                     return null;
275                 }
276             };
277             class B5 {
278             }
279         }
280     }
281 
282     public @interface A4 {
283 
284         public class B1 {
285         }
286 
287         public enum B2 {
288         }
289 
290         public interface B3 {
291         }
292 
293         public @interface B4 {
294         }
295     }
296 
297     {
A1()298         new A1() {
299             class B1 {
300             }
301 
302             public void a2() {
303                 new B1() {
304                 };
305                 class B5 {
306                 }
307             }
308         };
A3()309         new A3() {
310             class B1 {
311             }
312 
313             public void a3() {
314                 new B1() {
315                 };
316                 class B5 {
317                 }
318             }
319         };
A4()320         new A4() {
321             @Override
322             public Class<? extends Annotation> annotationType() {
323                 return null;
324             }
325 
326             class B1 {
327             }
328 
329             public void a4() {
330                 new B1() {
331                 };
332                 class B5 {
333                 }
334             }
335         };
336         Runnable r = () -> {
337             new A1() {
338             };
339             new A3() {
340             };
341             new A4() {
342                 @Override
343                 public Class<? extends Annotation> annotationType() {
344                     return null;
345                 }
346             };
347             class B5 {
348             }
349         };
350     }
351 
352     static {
A1()353         new A1() {
354             class B1 {
355             }
356 
357             public void a2() {
358                 new B1() {
359                 };
360                 class B5 {
361                 }
362             }
363         };
A3()364         new A3() {
365             class B1 {
366             }
367 
368             public void a3() {
369                 new B1() {
370                 };
371                 class B5 {
372                 }
373             }
374         };
A4()375         new A4() {
376             @Override
377             public Class<? extends Annotation> annotationType() {
378                 return null;
379             }
380 
381             class B1 {
382             }
383 
384             public void a4() {
385                 new B1() {
386                 };
387                 class B5 {
388                 }
389             }
390         };
391         Runnable r = () -> {
392             new A1() {
393             };
394             new A3() {
395             };
396             new A4() {
397                 @Override
398                 public Class<? extends Annotation> annotationType() {
399                     return null;
400                 }
401             };
402             class B5 {
403             }
404         };
405     }
406 
a5()407     public void a5() {
408         class A5 {
409 
410             class B1 {
411             }
412 
413             public void a5() {
414                 new B1() {
415                 };
416 
417                 class B5 {
418                 }
419             }
420         }
421         Runnable r = () -> {
422             new A1() {
423             };
424             new A3() {
425             };
426             new A4() {
427                 @Override
428                 public Class<? extends Annotation> annotationType() {
429                     return null;
430                 }
431             };
432             class B5 {
433             }
434         };
435     }
436 }
437