View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.bcel.classfile;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertNotNull;
21  import static org.junit.jupiter.api.Assertions.assertTrue;
22  import static org.junit.jupiter.api.Assertions.fail;
23  
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.net.URL;
27  import java.nio.file.Files;
28  import java.nio.file.Paths;
29  import java.util.Enumeration;
30  import java.util.regex.Matcher;
31  import java.util.regex.Pattern;
32  
33  import org.apache.bcel.util.SyntheticRepository;
34  import org.apache.commons.lang3.StringUtils;
35  import org.junit.jupiter.api.Test;
36  import org.junit.jupiter.params.ParameterizedTest;
37  import org.junit.jupiter.params.provider.ValueSource;
38  
39  /**
40   * Tests {@code module-info.class} files.
41   */
42  public class ConstantPoolModuleToStringTestCase {
43  
44      static class ToStringVisitor extends EmptyVisitor {
45  
46          private final StringBuilder stringBuilder = new StringBuilder();
47          private final ConstantPool pool;
48          private int count;
49  
50          public ToStringVisitor(final ConstantPool pool) {
51              this.pool = pool;
52          }
53  
54          private void append(final Object obj) {
55              if (!(stringBuilder.length() == 0)) {
56                  stringBuilder.append('\n');
57              }
58              stringBuilder.append(obj);
59          }
60  
61          @Override
62          public String toString() {
63              return "ToStringVisitor [count=" + count + ", stringBuilder=" + stringBuilder + ", pool=" + pool + "]";
64          }
65  
66          @Override
67          public void visitAnnotation(final Annotations obj) {
68              super.visitAnnotation(obj);
69              append(obj);
70          }
71  
72          @Override
73          public void visitAnnotationDefault(final AnnotationDefault obj) {
74              super.visitAnnotationDefault(obj);
75              append(obj);
76          }
77  
78          @Override
79          public void visitAnnotationEntry(final AnnotationEntry obj) {
80              super.visitAnnotationEntry(obj);
81              append(obj);
82          }
83  
84          @Override
85          public void visitBootstrapMethods(final BootstrapMethods obj) {
86              super.visitBootstrapMethods(obj);
87              append(obj);
88          }
89  
90          @Override
91          public void visitCode(final Code obj) {
92              super.visitCode(obj);
93              append(obj.toString(true));
94          }
95  
96          @Override
97          public void visitCodeException(final CodeException obj) {
98              super.visitCodeException(obj);
99              append(obj.toString(pool, true));
100         }
101 
102         @Override
103         public void visitConstantClass(final ConstantClass obj) {
104             super.visitConstantClass(obj);
105             append(obj);
106         }
107 
108         @Override
109         public void visitConstantDouble(final ConstantDouble obj) {
110             super.visitConstantDouble(obj);
111             append(obj);
112         }
113 
114         @Override
115         public void visitConstantDynamic(final ConstantDynamic constantDynamic) {
116             super.visitConstantDynamic(constantDynamic);
117             append(constantDynamic);
118         }
119 
120         @Override
121         public void visitConstantFieldref(final ConstantFieldref obj) {
122             super.visitConstantFieldref(obj);
123             append(obj);
124         }
125 
126         @Override
127         public void visitConstantFloat(final ConstantFloat obj) {
128             super.visitConstantFloat(obj);
129             append(obj);
130         }
131 
132         @Override
133         public void visitConstantInteger(final ConstantInteger obj) {
134             super.visitConstantInteger(obj);
135             append(obj);
136         }
137 
138         @Override
139         public void visitConstantInterfaceMethodref(final ConstantInterfaceMethodref obj) {
140             super.visitConstantInterfaceMethodref(obj);
141             append(obj);
142         }
143 
144         @Override
145         public void visitConstantInvokeDynamic(final ConstantInvokeDynamic obj) {
146             super.visitConstantInvokeDynamic(obj);
147             append(obj);
148         }
149 
150         @Override
151         public void visitConstantLong(final ConstantLong obj) {
152             super.visitConstantLong(obj);
153             append(obj);
154         }
155 
156         @Override
157         public void visitConstantMethodHandle(final ConstantMethodHandle obj) {
158             super.visitConstantMethodHandle(obj);
159             append(obj);
160         }
161 
162         @Override
163         public void visitConstantMethodref(final ConstantMethodref obj) {
164             super.visitConstantMethodref(obj);
165             append(obj);
166         }
167 
168         @Override
169         public void visitConstantMethodType(final ConstantMethodType obj) {
170             super.visitConstantMethodType(obj);
171             append(obj);
172         }
173 
174         @Override
175         public void visitConstantModule(final ConstantModule constantModule) {
176             super.visitConstantModule(constantModule);
177             append(constantModule);
178         }
179 
180         @Override
181         public void visitConstantNameAndType(final ConstantNameAndType obj) {
182             super.visitConstantNameAndType(obj);
183             append(obj);
184         }
185 
186         @Override
187         public void visitConstantPackage(final ConstantPackage constantPackage) {
188             super.visitConstantPackage(constantPackage);
189             append(constantPackage);
190         }
191 
192         @Override
193         public void visitConstantPool(final ConstantPool obj) {
194             super.visitConstantPool(obj);
195             append(obj);
196         }
197 
198         @Override
199         public void visitConstantString(final ConstantString obj) {
200             super.visitConstantString(obj);
201             append(obj);
202         }
203 
204         @Override
205         public void visitConstantUtf8(final ConstantUtf8 obj) {
206             super.visitConstantUtf8(obj);
207             append(obj);
208         }
209 
210         @Override
211         public void visitConstantValue(final ConstantValue obj) {
212             super.visitConstantValue(obj);
213             append(obj);
214         }
215 
216         @Override
217         public void visitDeprecated(final Deprecated obj) {
218             super.visitDeprecated(obj);
219             append(obj);
220         }
221 
222         @Override
223         public void visitEnclosingMethod(final EnclosingMethod obj) {
224             super.visitEnclosingMethod(obj);
225             append(obj);
226         }
227 
228         @Override
229         public void visitExceptionTable(final ExceptionTable obj) {
230             super.visitExceptionTable(obj);
231             append(obj);
232         }
233 
234         @Override
235         public void visitField(final Field obj) {
236             super.visitField(obj);
237             append(obj);
238         }
239 
240         @Override
241         public void visitInnerClass(final InnerClass obj) {
242             super.visitInnerClass(obj);
243             append(obj.toString(pool));
244         }
245 
246         @Override
247         public void visitInnerClasses(final InnerClasses obj) {
248             super.visitInnerClasses(obj);
249             append(obj);
250         }
251 
252         @Override
253         public void visitJavaClass(final JavaClass obj) {
254             super.visitJavaClass(obj);
255             append(obj);
256         }
257 
258         @Override
259         public void visitLineNumber(final LineNumber obj) {
260             super.visitLineNumber(obj);
261             append(obj);
262         }
263 
264         @Override
265         public void visitLineNumberTable(final LineNumberTable obj) {
266             super.visitLineNumberTable(obj);
267             append(obj);
268         }
269 
270         @Override
271         public void visitLocalVariable(final LocalVariable obj) {
272             super.visitLocalVariable(obj);
273             append(obj);
274         }
275 
276         @Override
277         public void visitLocalVariableTable(final LocalVariableTable obj) {
278             super.visitLocalVariableTable(obj);
279             append(obj);
280         }
281 
282         @Override
283         public void visitLocalVariableTypeTable(final LocalVariableTypeTable obj) {
284             super.visitLocalVariableTypeTable(obj);
285             append(obj);
286         }
287 
288         @Override
289         public void visitMethod(final Method obj) {
290             super.visitMethod(obj);
291             append(obj);
292         }
293 
294         @Override
295         public void visitMethodParameter(final MethodParameter obj) {
296             super.visitMethodParameter(obj);
297             append(obj);
298         }
299 
300         @Override
301         public void visitMethodParameters(final MethodParameters obj) {
302             super.visitMethodParameters(obj);
303             append(obj);
304         }
305 
306         @Override
307         public void visitModule(final Module constantModule) {
308             super.visitModule(constantModule);
309             final String s = constantModule.toString();
310             final Matcher matcher = Pattern.compile("  (\\w+)([:(])").matcher(s);
311             while (matcher.find()) {
312                 switch (matcher.group(2)) {
313                     case ":":
314                         assertTrue(StringUtils.containsAny(matcher.group(1), "name", "flags", "version"));
315                         break;
316                     case "(":
317                         assertTrue(StringUtils.containsAny(matcher.group(1), "requires", "exports", "opens", "uses", "provides"));
318                         break;
319                     default:
320                         break;
321                 }
322             }
323             append(constantModule);
324         }
325 
326         @Override
327         public void visitModuleExports(final ModuleExports constantModule) {
328             super.visitModuleExports(constantModule);
329             append(constantModule);
330             final String s = constantModule.toString(pool);
331             final String[] tokens = s.split(", ");
332             assertNotNull(tokens);
333             assertEquals(3, tokens.length);
334             assertEquals("0000", tokens[1]);
335             final Matcher matcher = Pattern.compile("to\\((\\d+)\\):").matcher(tokens[2]);
336             assertTrue(matcher.find());
337             assertEquals(Integer.parseInt(matcher.group(1)), StringUtils.countMatches(s, '\n'));
338         }
339 
340         @Override
341         public void visitModuleMainClass(final ModuleMainClass obj) {
342             super.visitModuleMainClass(obj);
343             append(obj);
344         }
345 
346         @Override
347         public void visitModuleOpens(final ModuleOpens constantModule) {
348             super.visitModuleOpens(constantModule);
349             append(constantModule);
350             final String s = constantModule.toString(pool);
351             final String[] tokens = s.split(", ");
352             assertNotNull(tokens);
353             assertEquals(3, tokens.length);
354             assertEquals("0000", tokens[1]);
355             final Matcher matcher = Pattern.compile("to\\((\\d+)\\):").matcher(tokens[2]);
356             assertTrue(matcher.find());
357             assertEquals(Integer.parseInt(matcher.group(1)), StringUtils.countMatches(s, '\n'));
358         }
359 
360         @Override
361         public void visitModulePackages(final ModulePackages constantModule) {
362             super.visitModulePackages(constantModule);
363             append(constantModule);
364             final String s = constantModule.toString();
365             assertEquals(constantModule.getNumberOfPackages(), StringUtils.countMatches(s, '\n'));
366         }
367 
368         @Override
369         public void visitModuleProvides(final ModuleProvides constantModule) {
370             super.visitModuleProvides(constantModule);
371             append(constantModule);
372             final String s = constantModule.toString(pool);
373             final String[] tokens = s.split(", ");
374             assertNotNull(tokens);
375             assertEquals(2, tokens.length);
376             final Matcher matcher = Pattern.compile("with\\((\\d+)\\):").matcher(tokens[1]);
377             assertTrue(matcher.find());
378             assertEquals(Integer.parseInt(matcher.group(1)), StringUtils.countMatches(s, '\n'));
379             append(s);
380         }
381 
382         @Override
383         public void visitModuleRequires(final ModuleRequires constantModule) {
384             super.visitModuleRequires(constantModule);
385             append(constantModule);
386             append(constantModule.toString(pool));
387             final String s = constantModule.toString(pool).trim();
388             final boolean condition = StringUtils.startsWithAny(s,
389                     "jdk.",
390                     "java.",
391                     "org.junit",
392                     "org.apiguardian.api",
393                     "org.opentest4j",
394                     "net.bytebuddy",
395                     "com.sun.jna",
396                     "junit",
397                     "org.hamcrest");
398             assertTrue(condition, s);
399         }
400 
401         @Override
402         public void visitNestHost(final NestHost obj) {
403             super.visitNestHost(obj);
404             append(obj);
405         }
406 
407         @Override
408         public void visitNestMembers(final NestMembers obj) {
409             super.visitNestMembers(obj);
410             append(obj);
411         }
412 
413         @Override
414         public void visitParameterAnnotation(final ParameterAnnotations obj) {
415             super.visitParameterAnnotation(obj);
416             append(obj);
417         }
418 
419         @Override
420         public void visitParameterAnnotationEntry(final ParameterAnnotationEntry obj) {
421             super.visitParameterAnnotationEntry(obj);
422             append(obj);
423         }
424 
425         @Override
426         public void visitSignature(final Signature obj) {
427             super.visitSignature(obj);
428             append(obj);
429         }
430 
431         @Override
432         public void visitSourceFile(final SourceFile obj) {
433             super.visitSourceFile(obj);
434             append(obj);
435         }
436 
437         @Override
438         public void visitStackMap(final StackMap obj) {
439             super.visitStackMap(obj);
440             append(obj);
441         }
442 
443         @Override
444         public void visitStackMapEntry(final StackMapEntry obj) {
445             super.visitStackMapEntry(obj);
446             append(obj);
447         }
448 
449         @Override
450         public void visitSynthetic(final Synthetic obj) {
451             super.visitSynthetic(obj);
452             append(obj);
453         }
454 
455         @Override
456         public void visitUnknown(final Unknown obj) {
457             super.visitUnknown(obj);
458             append(obj);
459         }
460     }
461 
462     private static void test(final InputStream inputStream) throws IOException {
463         final ClassParser classParser = new ClassParser(inputStream, "module-info.class");
464         final JavaClass javaClass = classParser.parse();
465         testJavaClass(javaClass);
466     }
467 
468     private static void testJavaClass(final JavaClass javaClass) {
469         final ConstantPool constantPool = javaClass.getConstantPool();
470         final ToStringVisitor visitor = new ToStringVisitor(constantPool);
471         final DescendingVisitor descendingVisitor = new DescendingVisitor(javaClass, visitor);
472         try {
473             javaClass.accept(descendingVisitor);
474             assertNotNull(visitor.toString());
475         } catch (Exception | Error e) {
476             fail(visitor.toString(), e);
477         }
478     }
479 
480     @Test
481     public void test() throws Exception {
482         final Enumeration<URL> moduleURLs = getClass().getClassLoader().getResources("module-info.class");
483         while (moduleURLs.hasMoreElements()) {
484             final URL url = moduleURLs.nextElement();
485             try (InputStream inputStream = url.openStream()) {
486                 test(inputStream);
487             }
488         }
489     }
490 
491     @ParameterizedTest
492     @ValueSource(strings = {
493     // @formatter:off
494         "src/test/resources/jpms/java11/commons-io/module-info.class",
495         "src/test/resources/jpms/java17/commons-io/module-info.class",
496         "src/test/resources/jpms/java18/commons-io/module-info.class",
497         "src/test/resources/jpms/java19-ea/commons-io/module-info.class"})
498     // @formatter:on
499     public void test(final String first) throws Exception {
500         try (final InputStream inputStream = Files.newInputStream(Paths.get(first))) {
501             test(inputStream);
502         }
503     }
504 
505     @ParameterizedTest
506     @ValueSource(strings = {
507     // @formatter:off
508         "java.lang.CharSequence$1CharIterator",                 // contains attribute EnclosingMethod
509         "org.apache.commons.lang3.function.TriFunction",        // contains attributes BootstrapMethods, InnerClasses, LineNumberTable, LocalVariableTable,
510                                                                 // LocalVariableTypeTable, RuntimeVisibleAnnotations, Signature, SourceFile
511         "org.apache.commons.lang3.math.NumberUtils",            // contains attribute ConstantFloat, ConstantDouble
512         "org.apache.bcel.Const",                                // contains attributes MethodParameters
513         "java.io.StringBufferInputStream",                      // contains attributes Deprecated, StackMap
514         "java.nio.file.Files",                                  // contains attributes ConstantValue, ExceptionTable, NestMembers
515         "org.junit.jupiter.api.AssertionsKt",                   // contains attribute ParameterAnnotation
516         "javax.annotation.ManagedBean",                         // contains attribute AnnotationDefault
517         "javax.management.remote.rmi.RMIConnectionImpl_Stub"})  // contains attribute Synthetic
518     // @formatter:on
519     public void testClass(final String className) throws Exception {
520         testJavaClass(SyntheticRepository.getInstance().loadClass(className));
521     }
522 }