Skip to content

Commit

Permalink
Fixes JDK-8240567 by splitting up genModuleDescriptorsMethod(ClassBui…
Browse files Browse the repository at this point in the history
…lder)

Co-authored-by: Christoph Schwentker <siedlerkiller@gmail.com>
  • Loading branch information
koppor and Siedlerchr committed Jun 6, 2023
1 parent 4a75fd4 commit 4d55c5d
Show file tree
Hide file tree
Showing 2 changed files with 201 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -666,23 +666,92 @@ 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<List<ModuleInfo>> splitModuleInfos = new ArrayList<>();
List<ModuleInfo> 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<ModuleInfo> 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()),
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)
.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();
});
}
Expand Down
121 changes: 121 additions & 0 deletions test/jdk/tools/jlink/JLink100Modules.java
Original file line number Diff line number Diff line change
@@ -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();
}
}

0 comments on commit 4d55c5d

Please sign in to comment.