From 4d55c5d250630425158c418ee1f6a0cebb6ee4e3 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Tue, 6 Jun 2023 21:47:25 +0200 Subject: [PATCH] Fixes JDK-8240567 by splitting up genModuleDescriptorsMethod(ClassBuilder) Co-authored-by: Christoph Schwentker --- .../internal/plugins/SystemModulesPlugin.java | 91 +++++++++++-- test/jdk/tools/jlink/JLink100Modules.java | 121 ++++++++++++++++++ 2 files changed, 201 insertions(+), 11 deletions(-) create mode 100644 test/jdk/tools/jlink/JLink100Modules.java diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java index c01042bdc94e7..288695ad8f380 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java @@ -118,7 +118,7 @@ public final class SystemModulesPlugin extends AbstractPlugin { ClassDesc.ofInternalName("jdk/internal/module/SystemModules"); private static final ClassDesc CD_SYSTEM_MODULES_MAP = ClassDesc.ofInternalName(SYSTEM_MODULES_MAP_CLASSNAME); - private boolean enabled; + private final boolean enabled; public SystemModulesPlugin() { super("system-modules"); @@ -666,6 +666,76 @@ private void genIncubatorModules(ClassBuilder clb) { * Generate bytecode for moduleDescriptors method */ private void genModuleDescriptorsMethod(ClassBuilder clb) { + if (moduleInfos.size() <= 10) { + clb.withMethodBody( + "moduleDescriptors", + MethodTypeDesc.of(CD_MODULE_DESCRIPTOR.arrayType()), + ACC_PUBLIC, + cob -> { + cob.constantInstruction(moduleInfos.size()) + .anewarray(CD_MODULE_DESCRIPTOR) + .astore(MD_VAR); + for (int index = 0; index < moduleInfos.size(); index++) { + ModuleInfo minfo = moduleInfos.get(index); + new ModuleDescriptorBuilder(cob, + minfo.descriptor(), + minfo.packages(), + index).build(); + } + cob.aload(MD_VAR) + .areturn(); + }); + return; + } + + // split up module infos in "consumable" packages + List> splitModuleInfos = new ArrayList<>(); + List currentModuleInfos = null; + for (int index = 0; index < moduleInfos.size(); index++) { + // The method is "manually split" based on the heuristics that 90 ModuleDescriptors are smaller than 64kb + // The number 10 is chosen "randomly" to be below the 64kb limit of a method + if (index % 10 == 0) { + // Prepare new list + currentModuleInfos = new ArrayList<>(); + splitModuleInfos.add(currentModuleInfos); + } + currentModuleInfos.add(moduleInfos.get(index)); + } + + // generate all helper methods + final String helperMethodNamePrefix = "moduleDescriptorsSub"; + final int[] globalCount = {0}; + for (final int[] index = {0}; index[0] < splitModuleInfos.size(); index[0]++) { + clb.withMethodBody( + helperMethodNamePrefix + index[0], + MethodTypeDesc.of(CD_void, CD_MODULE_DESCRIPTOR.arrayType()), + ACC_PUBLIC, + cob -> { + List moduleInfosPackage = splitModuleInfos.get(index[0]); + if (index[0] > 0) { + // call last helper method + cob.aload(0) + .aload(1) // load first parameter, which is MD_VAR + .invokevirtual( + this.classDesc, + helperMethodNamePrefix + (index[0] - 1), + MethodTypeDesc.of(CD_void, CD_MODULE_DESCRIPTOR.arrayType()) + ); + } + for (int j = 0; j < moduleInfosPackage.size(); j++) { + ModuleInfo minfo = moduleInfosPackage.get(j); + // executed after the call, thus it is OK to overwrite index 0 (BUILDER_VAR) + new ModuleDescriptorBuilder(cob, + minfo.descriptor(), + minfo.packages(), + globalCount[0]).build(); + globalCount[0]++; + } + cob.return_(); + }); + } + + // generate call to last helper method clb.withMethodBody( "moduleDescriptors", MethodTypeDesc.of(CD_MODULE_DESCRIPTOR.arrayType()), @@ -673,16 +743,15 @@ private void genModuleDescriptorsMethod(ClassBuilder clb) { cob -> { cob.constantInstruction(moduleInfos.size()) .anewarray(CD_MODULE_DESCRIPTOR) - .astore(MD_VAR); - - for (int index = 0; index < moduleInfos.size(); index++) { - ModuleInfo minfo = moduleInfos.get(index); - new ModuleDescriptorBuilder(cob, - minfo.descriptor(), - minfo.packages(), - index).build(); - } - cob.aload(MD_VAR) + .astore(MD_VAR) + .aload(MD_VAR) // storing for the return at the end + .aload(0) + .aload(MD_VAR) + .invokevirtual( + this.classDesc, + helperMethodNamePrefix + (splitModuleInfos.size() - 1), + MethodTypeDesc.of(CD_void, CD_MODULE_DESCRIPTOR.arrayType()) + ) .areturn(); }); } diff --git a/test/jdk/tools/jlink/JLink100Modules.java b/test/jdk/tools/jlink/JLink100Modules.java new file mode 100644 index 0000000000000..4a6111505d272 --- /dev/null +++ b/test/jdk/tools/jlink/JLink100Modules.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.StringJoiner; +import java.util.spi.ToolProvider; +import tests.JImageGenerator; +import tests.JImageGenerator.JLinkTask; +/* + * @test + * @summary Make sure that 100 modules can be linked using jlink. + * @bug 8240567 + * @library ../lib + * @modules java.base/jdk.internal.jimage + * jdk.jdeps/com.sun.tools.classfile + * jdk.jlink/jdk.tools.jlink.internal + * jdk.jlink/jdk.tools.jlink.plugin + * jdk.jlink/jdk.tools.jmod + * jdk.jlink/jdk.tools.jimage + * jdk.compiler + * @build tests.* + * @run main/othervm -verbose:gc -Xmx1g -Xlog:init=debug -XX:+UnlockDiagnosticVMOptions -XX:+BytecodeVerificationLocal JLink100Modules + */ +public class JLink100Modules { + private static final ToolProvider JAVAC_TOOL = ToolProvider.findFirst("javac") + .orElseThrow(() -> new RuntimeException("javac tool not found")); + private static final ToolProvider JLINK_TOOL = ToolProvider.findFirst("jlink") + .orElseThrow(() -> new RuntimeException("jlink tool not found")); + + static void report(String command, String[] args) { + System.out.println(command + " " + String.join(" ", Arrays.asList(args))); + } + + static void javac(String[] args) { + report("javac", args); + JAVAC_TOOL.run(System.out, System.err, args); + } + + static void jlink(String[] args) { + report("jlink", args); + JLINK_TOOL.run(System.out, System.err, args); + } + + public static void main(String[] args) throws Exception { + Path src = Paths.get("bug8240567"); + + StringJoiner mainModuleInfoContent = new StringJoiner(";\n requires ", "module bug8240567x {\n requires ", "\n;}"); + + // create 100 modules. With this naming schema up to 130 seem to work + for (int i = 0; i < 150; i++) { + String name = "module" + i + "x"; + Path moduleDir = Files.createDirectories(src.resolve(name)); + + StringBuilder builder = new StringBuilder("module "); + builder.append(name).append(" {"); + + for (int j = 0; j < i; j++) { + builder.append("requires module" + j + "x;"); + } + builder.append("}\n"); + Files.writeString(moduleDir.resolve("module-info.java"), builder.toString()); + mainModuleInfoContent.add(name); + } + + // create module reading the generated modules + Path mainModulePath = src.resolve("bug8240567x"); + Files.createDirectories(mainModulePath); + Path mainModuleInfo = mainModulePath.resolve("module-info.java"); + Files.writeString(mainModuleInfo, mainModuleInfoContent.toString()); + + Path mainClassDir = mainModulePath.resolve("testpackage"); + Files.createDirectories(mainClassDir); + + Files.writeString(mainClassDir.resolve("JLink100ModulesTest.java"), """ + package testpackage; + + public class JLink100ModulesTest { + public static void main(String[] args) throws Exception { + System.out.println("JLink100ModulesTest started."); + } + } + """); + + String out = src.resolve("out").toString(); + + javac(new String[] { + "-d", out, + "--module-source-path", src.toString(), + "--module", "bug8240567x" + }); + + JImageGenerator.getJLinkTask() + .modulePath(out) + .output(src.resolve("out-jlink")) + .addMods("bug8240567x") + .call().assertSuccess(); + } +}