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   *   http://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.maven.plugin.compiler;
20  
21  import java.util.ArrayList;
22  import java.util.HashSet;
23  import java.util.List;
24  import java.util.Set;
25  
26  import org.apache.maven.plugin.logging.Log;
27  import org.objectweb.asm.ClassReader;
28  import org.objectweb.asm.ClassVisitor;
29  import org.objectweb.asm.ClassWriter;
30  import org.objectweb.asm.ModuleVisitor;
31  import org.objectweb.asm.Opcodes;
32  
33  /**
34   * {@code module-info.class} bytecode transformer for
35   * <a href="https://issues.apache.org/jira/browse/MCOMPILER-542">MCOMPILER-542</a>:
36   * drops detailed JDK version.
37   */
38  final class ModuleInfoTransformer {
39  
40      private ModuleInfoTransformer() {}
41  
42      static byte[] transform(byte[] originalBytecode, String javaVersion, Log log) {
43          List<String> modulesModified = new ArrayList<>();
44          Set<String> foundVersions = new HashSet<>();
45          ClassReader reader = new ClassReader(originalBytecode);
46          ClassWriter writer = new ClassWriter(0);
47  
48          ClassVisitor classVisitor = new ClassVisitor(Opcodes.ASM9, writer) {
49              @Override
50              public ModuleVisitor visitModule(String name, int access, String version) {
51                  ModuleVisitor originalModuleVisitor = super.visitModule(name, access, version);
52                  return new ModuleVisitor(Opcodes.ASM9, originalModuleVisitor) {
53                      @Override
54                      public void visitRequire(String module, int access, String version) {
55                          // Check if the module name matches the java/jdk modules
56                          if (version != null && (module.startsWith("java.") || module.startsWith("jdk."))) {
57                              // Patch the version from the java.* and jdk.* modules
58                              // with the --release N version.
59                              super.visitRequire(module, access, javaVersion);
60                              foundVersions.add(version);
61                              modulesModified.add(module);
62                          } else {
63                              // Keep the original require statement
64                              super.visitRequire(module, access, version);
65                          }
66                      }
67                  };
68              }
69          };
70  
71          reader.accept(classVisitor, 0);
72  
73          if (modulesModified.isEmpty()) {
74              return null;
75          }
76  
77          log.info(String.format(
78                  "JDK-8318913 workaround: patched module-info.class requires version from %s to [%s] on %d JDK modules %s",
79                  foundVersions, javaVersion, modulesModified.size(), modulesModified));
80  
81          return writer.toByteArray();
82      }
83  }