View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.bcel.classfile;
20  
21  import static org.junit.jupiter.api.Assertions.assertEquals;
22  import static org.junit.jupiter.api.Assertions.assertTrue;
23  
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.net.URL;
27  import java.util.ArrayList;
28  import java.util.Arrays;
29  import java.util.HashMap;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.Set;
33  import java.util.TreeSet;
34  import java.util.stream.Stream;
35  
36  import org.apache.bcel.Const;
37  import org.apache.commons.lang3.stream.Streams;
38  import org.junit.jupiter.params.ParameterizedTest;
39  import org.junit.jupiter.params.provider.MethodSource;
40  
41  /**
42   * Tests {@code module-info.class} files.
43   */
44  public final class ConstantPoolModuleAccessTest {
45  
46      static Stream<URL> testJREModules() throws IOException {
47          return Streams.of(ConstantPoolModuleAccessTest.class.getClassLoader().getResources("module-info.class"));
48      }
49  
50      @ParameterizedTest
51      @MethodSource
52      void testJREModules(final URL url) throws Exception {
53          try (InputStream inputStream = url.openStream()) {
54              final ClassParser classParser = new ClassParser(inputStream, "module-info.class");
55              final JavaClass javaClass = classParser.parse();
56              final ConstantPool constantPool = javaClass.getConstantPool();
57              final EmptyVisitor visitor = new EmptyVisitor() {
58                  @Override
59                  public void visitModule(final Module obj) {
60                      final String urlPath = url.getPath();
61                      if (urlPath.contains("/commons-")) {
62                          assertEquals(4096, obj.getModuleFlags(), url.toString());
63                      } else {
64                          assertEquals(0, obj.getModuleFlags(), url.toString());
65                      }
66                      final String[] usedClassNames = obj.getUsedClassNames(constantPool, true);
67                      if (urlPath.contains("junit-jupiter-engine")) {
68                          assertEquals(1, usedClassNames.length);
69                          assertEquals("org.junit.jupiter.api.extension.Extension", usedClassNames[0]);
70                      } else if (urlPath.contains("junit-platform-launcher")) {
71                          final List<String> expected = new ArrayList<>();
72                          expected.add("org.junit.platform.engine.TestEngine");
73                          expected.add("org.junit.platform.launcher.LauncherDiscoveryListener");
74                          expected.add("org.junit.platform.launcher.LauncherInterceptor");
75                          expected.add("org.junit.platform.launcher.LauncherSessionListener");
76                          expected.add("org.junit.platform.launcher.PostDiscoveryFilter");
77                          expected.add("org.junit.platform.launcher.TestExecutionListener");
78                          assertEquals(expected, Arrays.asList(usedClassNames));
79                      } else if (urlPath.contains("junit-platform-common")) {
80                          final List<String> expected = new ArrayList<>();
81                          expected.add("org.junit.platform.commons.support.scanning.ClasspathScanner");
82                          assertEquals(expected, Arrays.asList(usedClassNames));
83                      } else if (urlPath.contains("junit-platform-engine")) {
84                          final List<String> expected = new ArrayList<>();
85                          expected.add("org.junit.platform.engine.discovery.DiscoverySelectorIdentifierParser");
86                          assertEquals(expected, Arrays.asList(usedClassNames));
87                      } else if (urlPath.contains("/java.rmi/module-info.class")) {
88                          final List<String> expected = new ArrayList<>();
89                          expected.add("java.rmi.server.RMIClassLoaderSpi");
90                          assertEquals(expected, Arrays.asList(usedClassNames));
91                      } else if (urlPath.contains("/java.xml/module-info.class")) {
92                          final List<String> expected = new ArrayList<>();
93                          expected.add("javax.xml.datatype.DatatypeFactory");
94                          expected.add("javax.xml.parsers.DocumentBuilderFactory");
95                          expected.add("javax.xml.parsers.SAXParserFactory");
96                          expected.add("javax.xml.stream.XMLEventFactory");
97                          expected.add("javax.xml.stream.XMLInputFactory");
98                          expected.add("javax.xml.stream.XMLOutputFactory");
99                          expected.add("javax.xml.transform.TransformerFactory");
100                         expected.add("javax.xml.validation.SchemaFactory");
101                         expected.add("javax.xml.xpath.XPathFactory");
102                         expected.add("org.xml.sax.XMLReader");
103                         assertEquals(expected, Arrays.asList(usedClassNames));
104                     } else if (urlPath.contains("/java.datatransfer/module-info.class")) {
105                         final List<String> expected = new ArrayList<>();
106                         expected.add("sun.datatransfer.DesktopDatatransferService");
107                         assertEquals(expected, Arrays.asList(usedClassNames));
108                     } else if (urlPath.contains("/java.desktop/module-info.class")) {
109                         final List<String> expected = new ArrayList<>();
110                         expected.add("java.awt.im.spi.InputMethodDescriptor");
111                         expected.add("javax.accessibility.AccessibilityProvider");
112                         expected.add("javax.imageio.spi.ImageInputStreamSpi");
113                         expected.add("javax.imageio.spi.ImageOutputStreamSpi");
114                         expected.add("javax.imageio.spi.ImageReaderSpi");
115                         expected.add("javax.imageio.spi.ImageTranscoderSpi");
116                         expected.add("javax.imageio.spi.ImageWriterSpi");
117                         expected.add("javax.print.PrintServiceLookup");
118                         expected.add("javax.print.StreamPrintServiceFactory");
119                         expected.add("javax.sound.midi.spi.MidiDeviceProvider");
120                         expected.add("javax.sound.midi.spi.MidiFileReader");
121                         expected.add("javax.sound.midi.spi.MidiFileWriter");
122                         expected.add("javax.sound.midi.spi.SoundbankReader");
123                         expected.add("javax.sound.sampled.spi.AudioFileReader");
124                         expected.add("javax.sound.sampled.spi.AudioFileWriter");
125                         expected.add("javax.sound.sampled.spi.FormatConversionProvider");
126                         expected.add("javax.sound.sampled.spi.MixerProvider");
127                         expected.add("sun.swing.InteropProvider");
128                         assertEquals(expected, Arrays.asList(usedClassNames));
129                     } else if (urlPath.contains("/java.naming/module-info.class")) {
130                         final List<String> expected = new ArrayList<>();
131                         expected.add("javax.naming.ldap.StartTlsResponse");
132                         expected.add("javax.naming.spi.InitialContextFactory");
133                         if (javaClass.getMajor() > Const.MAJOR_11) {
134                             expected.add("javax.naming.ldap.spi.LdapDnsProvider");
135                         }
136                         assertEquals(expected, Arrays.asList(usedClassNames));
137                     } else if (urlPath.contains("/java.prefs/module-info.class")) {
138                         final List<String> expected = new ArrayList<>();
139                         expected.add("java.util.prefs.PreferencesFactory");
140                         assertEquals(expected, Arrays.asList(usedClassNames));
141                     } else if (urlPath.contains("/java.base/module-info.class")) {
142                         final List<String> expected = new ArrayList<>();
143                         expected.add("java.lang.System$LoggerFinder");
144                         expected.add("java.net.ContentHandlerFactory");
145                         if (javaClass.getMajor() > Const.MAJOR_17) {
146                             expected.add("java.net.spi.InetAddressResolverProvider");
147                         }
148                         expected.add("java.net.spi.URLStreamHandlerProvider");
149                         expected.add("java.nio.channels.spi.AsynchronousChannelProvider");
150                         expected.add("java.nio.channels.spi.SelectorProvider");
151                         expected.add("java.nio.charset.spi.CharsetProvider");
152                         expected.add("java.nio.file.spi.FileSystemProvider");
153                         expected.add("java.nio.file.spi.FileTypeDetector");
154                         expected.add("java.security.Provider");
155                         expected.add("java.text.spi.BreakIteratorProvider");
156                         expected.add("java.text.spi.CollatorProvider");
157                         expected.add("java.text.spi.DateFormatProvider");
158                         expected.add("java.text.spi.DateFormatSymbolsProvider");
159                         expected.add("java.text.spi.DecimalFormatSymbolsProvider");
160                         expected.add("java.text.spi.NumberFormatProvider");
161                         expected.add("java.time.chrono.AbstractChronology");
162                         expected.add("java.time.chrono.Chronology");
163                         expected.add("java.time.zone.ZoneRulesProvider");
164                         if (javaClass.getMajor() > Const.MAJOR_11 && javaClass.getMajor() < Const.MAJOR_24) {
165                             expected.add("java.util.random.RandomGenerator");
166                         }
167                         expected.add("java.util.spi.CalendarDataProvider");
168                         expected.add("java.util.spi.CalendarNameProvider");
169                         expected.add("java.util.spi.CurrencyNameProvider");
170                         expected.add("java.util.spi.LocaleNameProvider");
171                         expected.add("java.util.spi.ResourceBundleControlProvider");
172                         expected.add("java.util.spi.ResourceBundleProvider");
173                         expected.add("java.util.spi.TimeZoneNameProvider");
174                         expected.add("java.util.spi.ToolProvider");
175                         expected.add("javax.security.auth.spi.LoginModule");
176                         if (javaClass.getMajor() > Const.MAJOR_17) {
177                             expected.add("jdk.internal.io.JdkConsoleProvider");
178                         }
179                         expected.add("jdk.internal.logger.DefaultLoggerFinder");
180                         expected.add("sun.text.spi.JavaTimeDateTimePatternProvider");
181                         expected.add("sun.util.locale.provider.LocaleDataMetaInfo");
182                         expected.add("sun.util.resources.LocaleData$CommonResourceBundleProvider");
183                         expected.add("sun.util.resources.LocaleData$SupplementaryResourceBundleProvider");
184                         expected.add("sun.util.spi.CalendarProvider");
185                         assertEquals(expected, Arrays.asList(usedClassNames));
186                     } else if (urlPath.contains("/jdk.management.agent/module-info.class") && javaClass.getMajor() < Const.MAJOR_21) {
187                         final List<String> expected = new ArrayList<>();
188                         expected.add("jdk.internal.agent.spi.AgentProvider");
189                         assertEquals(expected, Arrays.asList(usedClassNames));
190                     } else if (urlPath.contains("/java.management/module-info.class")) {
191                         final List<String> expected = new ArrayList<>();
192                         expected.add("javax.management.remote.JMXConnectorProvider");
193                         expected.add("javax.management.remote.JMXConnectorServerProvider");
194                         expected.add("sun.management.spi.PlatformMBeanProvider");
195                         assertEquals(expected, Arrays.asList(usedClassNames));
196                     } else if (urlPath.contains("/java.sql/module-info.class")) {
197                         final List<String> expected = new ArrayList<>();
198                         expected.add("java.sql.Driver");
199                         assertEquals(expected, Arrays.asList(usedClassNames));
200                     } else if (urlPath.contains("/jdk.httpserver/module-info.class")) {
201                         final List<String> expected = new ArrayList<>();
202                         expected.add("com.sun.net.httpserver.spi.HttpServerProvider");
203                         assertEquals(expected, Arrays.asList(usedClassNames));
204                     } else if (urlPath.contains("/java.sql.rowset/module-info.class")) {
205                         final List<String> expected = new ArrayList<>();
206                         expected.add("javax.sql.rowset.RowSetFactory");
207                         assertEquals(expected, Arrays.asList(usedClassNames));
208                     } else if (urlPath.contains("/java.compiler/module-info.class")) {
209                         final List<String> expected = new ArrayList<>();
210                         expected.add("javax.tools.DocumentationTool");
211                         expected.add("javax.tools.JavaCompiler");
212                         assertEquals(expected, Arrays.asList(usedClassNames));
213                     } else if (urlPath.contains("/java.scripting/module-info.class")) {
214                         final List<String> expected = new ArrayList<>();
215                         expected.add("javax.script.ScriptEngineFactory");
216                         assertEquals(expected, Arrays.asList(usedClassNames));
217                     } else if (urlPath.contains("/jdk.dynalink/module-info.class")) {
218                         final List<String> expected = new ArrayList<>();
219                         expected.add("jdk.dynalink.linker.GuardingDynamicLinkerExporter");
220                         assertEquals(expected, Arrays.asList(usedClassNames));
221                     } else if (urlPath.contains("/jdk.jdi/module-info.class")) {
222                         final List<String> expected = new ArrayList<>();
223                         expected.add("com.sun.jdi.connect.Connector");
224                         expected.add("com.sun.jdi.connect.spi.TransportService");
225                         assertEquals(expected, Arrays.asList(usedClassNames));
226                     } else if (urlPath.contains("/jdk.compiler/module-info.class")) {
227                         final List<String> expected = new ArrayList<>();
228                         expected.add("javax.annotation.processing.Processor");
229                         expected.add("com.sun.source.util.Plugin");
230                         if (javaClass.getMajor() > Const.MAJOR_11) {
231                             expected.add("com.sun.tools.doclint.DocLint");
232                         }
233                         expected.add("com.sun.tools.javac.platform.PlatformProvider");
234                         if (javaClass.getMajor() > Const.MAJOR_23) {
235                             expected.add("com.sun.tools.javac.api.JavacTrees$DocCommentTreeTransformer");
236                         }
237                         assertEquals(expected, Arrays.asList(usedClassNames));
238                     } else if (urlPath.contains("/jdk.jconsole/module-info.class")) {
239                         final List<String> expected = new ArrayList<>();
240                         expected.add("com.sun.tools.jconsole.JConsolePlugin");
241                         assertEquals(expected, Arrays.asList(usedClassNames));
242                     } else if (urlPath.contains("/jdk.attach/module-info.class")) {
243                         final List<String> expected = new ArrayList<>();
244                         expected.add("com.sun.tools.attach.spi.AttachProvider");
245                         assertEquals(expected, Arrays.asList(usedClassNames));
246                     } else if (urlPath.contains("/jdk.jshell/module-info.class")) {
247                         final List<String> expected = new ArrayList<>();
248                         expected.add("jdk.jshell.spi.ExecutionControlProvider");
249                         expected.add("jdk.internal.editor.spi.BuildInEditorProvider");
250                         assertEquals(expected, Arrays.asList(usedClassNames));
251                     } else if (urlPath.contains("/jdk.internal.le/module-info.class")) {
252                         final List<String> expected = new ArrayList<>();
253                         assertEquals(expected, Arrays.asList(usedClassNames));
254                     } else if (urlPath.contains("/jdk.jlink/module-info.class")) {
255                         final List<String> expected = new ArrayList<>();
256                         expected.add("jdk.tools.jlink.plugin.Plugin");
257                         assertEquals(expected, Arrays.asList(usedClassNames));
258                     } else if (urlPath.contains("/jdk.internal.jvmstat/module-info.class")) {
259                         final List<String> expected = new ArrayList<>();
260                         expected.add("sun.jvmstat.monitor.MonitoredHostService");
261                         assertEquals(expected, Arrays.asList(usedClassNames));
262                     } else if (urlPath.contains("/jdk.jpackage/module-info.class")) {
263                         final List<String> expected = new ArrayList<>();
264                         expected.add("jdk.jpackage.internal.Bundler");
265                         expected.add("jdk.jpackage.internal.Bundlers");
266                         assertEquals(expected, Arrays.asList(usedClassNames));
267                     } else if (urlPath.contains("/jdk.naming.ldap/module-info.class")) {
268                         final List<String> expected = new ArrayList<>();
269                         expected.add("com.sun.jndi.ldap.spi.LdapDnsProvider");
270                         assertEquals(expected, Arrays.asList(usedClassNames));
271                     } else if (urlPath.contains("/jdk.jsobject/module-info.class") && javaClass.getMajor() == Const.MAJOR_11) {
272                         final List<String> expected = new ArrayList<>();
273                         expected.add("jdk.internal.netscape.javascript.spi.JSObjectProvider");
274                         assertEquals(expected, Arrays.asList(usedClassNames));
275                     } else if (urlPath.contains("/jdk.jdeps/module-info.class") && javaClass.getMajor() > Const.MAJOR_24) {
276                         final List<String> expected = new ArrayList<>();
277                         expected.add("com.sun.tools.javac.platform.PlatformProvider");
278                         assertEquals(expected, Arrays.asList(usedClassNames));
279                     } else {
280                         assertEquals(0, usedClassNames.length, () -> "Found " + Arrays.toString(usedClassNames) + " in " + urlPath);
281                     }
282                     super.visitModule(obj);
283                 }
284 
285                 @Override
286                 public void visitModuleExports(final ModuleExports obj) {
287                     assertEquals(0, obj.getExportsFlags(), url.toString());
288                     final String packageName = obj.getPackageName(constantPool);
289                     final String[] toModuleNames = obj.getToModuleNames(constantPool);
290                     if (url.getPath().contains("junit-platform-commons")) {
291                         final Set<String> expected = new TreeSet<>();
292                         expected.add("org.junit.jupiter.api");
293                         expected.add("org.junit.jupiter.engine");
294                         expected.add("org.junit.jupiter.migrationsupport");
295                         expected.add("org.junit.jupiter.params");
296                         expected.add("org.junit.platform.console");
297                         expected.add("org.junit.platform.engine");
298                         expected.add("org.junit.platform.launcher");
299                         expected.add("org.junit.platform.reporting");
300                         expected.add("org.junit.platform.runner");
301                         expected.add("org.junit.platform.suite.api");
302                         switch (packageName) {
303                         case "org.junit.platform.commons.util":
304                             expected.add("org.junit.platform.suite.commons");
305                             expected.add("org.junit.platform.suite.engine");
306                             expected.add("org.junit.platform.testkit");
307                             expected.add("org.junit.vintage.engine");
308                             expected.add("org.junit.platform.jfr");
309                             assertEquals(expected, new TreeSet<>(Arrays.asList(toModuleNames)));
310                             break;
311                         case "org.junit.platform.commons.logging":
312                             expected.add("org.junit.platform.suite.engine");
313                             expected.add("org.junit.platform.testkit");
314                             expected.add("org.junit.vintage.engine");
315                             assertEquals(expected, new TreeSet<>(Arrays.asList(toModuleNames)));
316                             break;
317                         default:
318                             assertEquals(0, toModuleNames.length);
319                             break;
320                         }
321                     }
322                     super.visitModuleExports(obj);
323                 }
324 
325                 @Override
326                 public void visitModuleOpens(final ModuleOpens obj) {
327                     assertEquals(0, obj.getOpensFlags(), url.toString());
328                     final String packageName = obj.getPackageName(constantPool);
329                     final String[] toModuleNames = obj.getToModuleNames(constantPool);
330                     final String urlPath = url.getPath();
331                     if (urlPath.contains("junit-jupiter-engine")) {
332                         assertEquals("org.junit.jupiter.engine.extension", packageName);
333                         assertEquals(1, toModuleNames.length);
334                         assertEquals("org.junit.platform.commons", toModuleNames[0]);
335                     } else if (urlPath.contains("junit-jupiter-api")) {
336                         assertEquals("org.junit.jupiter.api.condition", packageName);
337                         assertEquals(1, toModuleNames.length);
338                         assertEquals("org.junit.platform.commons", toModuleNames[0]);
339                     }
340                     super.visitModuleOpens(obj);
341                 }
342 
343                 @Override
344                 public void visitModuleProvides(final ModuleProvides obj) {
345                     final String interfaceName = obj.getInterfaceName(constantPool);
346                     final String[] implementationClassNames = obj.getImplementationClassNames(constantPool, true);
347                     final String urlPath = url.getPath();
348                     if (urlPath.contains("junit-jupiter-engine")) {
349                         assertEquals("org.junit.platform.engine.TestEngine", interfaceName);
350                         assertEquals(1, implementationClassNames.length);
351                         assertEquals("org.junit.jupiter.engine.JupiterTestEngine", implementationClassNames[0]);
352                     } else if (urlPath.contains("junit-platform-launcher")) {
353                         assertEquals("org.junit.platform.launcher.TestExecutionListener", interfaceName);
354                         assertEquals(1, implementationClassNames.length);
355                         assertEquals("org.junit.platform.launcher.listeners.UniqueIdTrackingListener", implementationClassNames[0]);
356                     }
357                     super.visitModuleProvides(obj);
358                 }
359 
360                 @Override
361                 public void visitModuleRequires(final ModuleRequires obj) {
362                     if (url.getPath().contains("junit-jupiter-engine")) {
363                         final String moduleName = obj.getModuleName(constantPool);
364                         final Map<String, Integer> expected = new HashMap<>();
365                         expected.put("java.base", 32768);
366                         expected.put("org.apiguardian.api", 64);
367                         expected.put("org.junit.jupiter.api", 0);
368                         expected.put("org.junit.platform.commons", 0);
369                         expected.put("org.junit.platform.engine", 0);
370                         expected.put("org.opentest4j", 0);
371                         assertTrue(expected.containsKey(moduleName));
372                         assertEquals(expected.get(moduleName), obj.getRequiresFlags(), moduleName);
373                     }
374                     super.visitModuleRequires(obj);
375                 }
376             };
377             javaClass.accept(new DescendingVisitor(javaClass, visitor));
378         }
379     }
380 }