Categories:
Audio (13)
Biotech (29)
Bytecode (36)
Database (77)
Framework (7)
Game (7)
General (507)
Graphics (53)
I/O (35)
IDE (2)
JAR Tools (102)
JavaBeans (21)
JDBC (121)
JDK (426)
JSP (20)
Logging (108)
Mail (58)
Messaging (8)
Network (84)
PDF (97)
Report (7)
Scripting (84)
Security (32)
Server (121)
Servlet (26)
SOAP (24)
Testing (54)
Web (15)
XML (322)
Collections:
Other Resources:
JDK 17 jdk.jlink.jmod - JLink Tool
JDK 17 jdk.jlink.jmod is the JMOD file for JDK 17 JLink tool,
which can be invoked by the "jlink" command.
JDK 17 JLink tool compiled class files are stored in \fyicenter\jdk-17.0.5\jmods\jdk.jlink.jmod.
JDK 17 JLink tool compiled class files are also linked and stored in the \fyicenter\jdk-17.0.5\lib\modules JImage file.
JDK 17 JLink tool source code files are stored in \fyicenter\jdk-17.0.5\lib\src.zip\jdk.jlink.
You can click and view the content of each source code file in the list below.
✍: FYIcenter
⏎ jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java
/*
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package jdk.tools.jlink.internal.plugins;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.module.Configuration;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleDescriptor.Exports;
import java.lang.module.ModuleDescriptor.Opens;
import java.lang.module.ModuleDescriptor.Provides;
import java.lang.module.ModuleDescriptor.Requires;
import java.lang.module.ModuleDescriptor.Version;
import java.lang.module.ModuleFinder;
import java.lang.module.ModuleReader;
import java.lang.module.ModuleReference;
import java.lang.module.ResolvedModule;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.function.IntSupplier;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import jdk.internal.module.Checks;
import jdk.internal.module.DefaultRoots;
import jdk.internal.module.Modules;
import jdk.internal.module.ModuleHashes;
import jdk.internal.module.ModuleInfo.Attributes;
import jdk.internal.module.ModuleInfoExtender;
import jdk.internal.module.ModuleReferenceImpl;
import jdk.internal.module.ModuleResolution;
import jdk.internal.module.ModuleTarget;
import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.ClassVisitor;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.ModuleVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
import jdk.tools.jlink.internal.ModuleSorter;
import jdk.tools.jlink.plugin.PluginException;
import jdk.tools.jlink.plugin.ResourcePool;
import jdk.tools.jlink.plugin.ResourcePoolBuilder;
import jdk.tools.jlink.plugin.ResourcePoolEntry;
/**
* Jlink plugin to reconstitute module descriptors and other attributes for system
* modules. The plugin generates implementations of SystemModules to avoid parsing
* module-info.class files at startup. It also generates SystemModulesMap to return
* the SystemModules implementation for a specific initial module.
*
* As a side effect, the plugin adds the ModulePackages class file attribute to the
* module-info.class files that don't have the attribute.
*
* @see jdk.internal.module.SystemModuleFinders
* @see jdk.internal.module.SystemModules
*/
public final class SystemModulesPlugin extends AbstractPlugin {
private static final String SYSTEM_MODULES_MAP_CLASS =
"jdk/internal/module/SystemModulesMap";
private static final String SYSTEM_MODULES_CLASS_PREFIX =
"jdk/internal/module/SystemModules$";
private static final String ALL_SYSTEM_MODULES_CLASS =
SYSTEM_MODULES_CLASS_PREFIX + "all";
private static final String DEFAULT_SYSTEM_MODULES_CLASS =
SYSTEM_MODULES_CLASS_PREFIX + "default";
private boolean enabled;
public SystemModulesPlugin() {
super("system-modules");
this.enabled = true;
}
@Override
public Set<State> getState() {
return enabled ? EnumSet.of(State.AUTO_ENABLED, State.FUNCTIONAL)
: EnumSet.of(State.DISABLED);
}
@Override
public boolean hasArguments() {
return true;
}
@Override
public void configure(Map<String, String> config) {
String arg = config.get(getName());
if (arg != null) {
throw new IllegalArgumentException(getName() + ": " + arg);
}
}
@Override
public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
if (!enabled) {
throw new PluginException(getName() + " was set");
}
// validate, transform (if needed), and add the module-info.class files
List<ModuleInfo> moduleInfos = transformModuleInfos(in, out);
// generate and add the SystemModuleMap and SystemModules classes
Set<String> generated = genSystemModulesClasses(moduleInfos, out);
// pass through all other resources
in.entries()
.filter(data -> !data.path().endsWith("/module-info.class")
&& !generated.contains(data.path()))
.forEach(data -> out.add(data));
return out.build();
}
/**
* Validates and transforms the module-info.class files in the modules, adding
* the ModulePackages class file attribute if needed.
*
* @return the list of ModuleInfo objects, the first element is java.base
*/
List<ModuleInfo> transformModuleInfos(ResourcePool in, ResourcePoolBuilder out) {
List<ModuleInfo> moduleInfos = new ArrayList<>();
// Sort modules in the topological order so that java.base is always first.
new ModuleSorter(in.moduleView()).sorted().forEach(module -> {
ResourcePoolEntry data = module.findEntry("module-info.class").orElseThrow(
// automatic modules not supported
() -> new PluginException("module-info.class not found for " +
module.name() + " module")
);
assert module.name().equals(data.moduleName());
try {
byte[] content = data.contentBytes();
Set<String> packages = module.packages();
ModuleInfo moduleInfo = new ModuleInfo(content, packages);
// link-time validation
moduleInfo.validateNames();
// check if any exported or open package is not present
moduleInfo.validatePackages();
// module-info.class may be overridden to add ModulePackages
if (moduleInfo.shouldRewrite()) {
data = data.copyWithContent(moduleInfo.getBytes());
}
moduleInfos.add(moduleInfo);
// add resource pool entry
out.add(data);
} catch (IOException e) {
throw new PluginException(e);
}
});
return moduleInfos;
}
/**
* Generates the SystemModules classes (at least one) and the SystemModulesMap
* class to map initial modules to a SystemModules class.
*
* @return the resource names of the resources added to the pool
*/
private Set<String> genSystemModulesClasses(List<ModuleInfo> moduleInfos,
ResourcePoolBuilder out) {
int moduleCount = moduleInfos.size();
ModuleFinder finder = finderOf(moduleInfos);
assert finder.findAll().size() == moduleCount;
// map of initial module name to SystemModules class name
Map<String, String> map = new LinkedHashMap<>();
// the names of resources written to the pool
Set<String> generated = new HashSet<>();
// generate the SystemModules implementation to reconstitute all modules
Set<String> allModuleNames = moduleInfos.stream()
.map(ModuleInfo::moduleName)
.collect(Collectors.toSet());
String rn = genSystemModulesClass(moduleInfos,
resolve(finder, allModuleNames),
ALL_SYSTEM_MODULES_CLASS,
out);
generated.add(rn);
// generate, if needed, a SystemModules class to reconstitute the modules
// needed for the case that the initial module is the unnamed module.
String defaultSystemModulesClassName;
Configuration cf = resolve(finder, DefaultRoots.compute(finder));
if (cf.modules().size() == moduleCount) {
// all modules are resolved so no need to generate a class
defaultSystemModulesClassName = ALL_SYSTEM_MODULES_CLASS;
} else {
defaultSystemModulesClassName = DEFAULT_SYSTEM_MODULES_CLASS;
rn = genSystemModulesClass(sublist(moduleInfos, cf),
cf,
defaultSystemModulesClassName,
out);
generated.add(rn);
}
// Generate a SystemModules class for each module with a main class
int suffix = 0;
for (ModuleInfo mi : moduleInfos) {
if (mi.descriptor().mainClass().isPresent()) {
String moduleName = mi.moduleName();
cf = resolve(finder, Set.of(moduleName));
if (cf.modules().size() == moduleCount) {
// resolves all modules so no need to generate a class
map.put(moduleName, ALL_SYSTEM_MODULES_CLASS);
} else {
String cn = SYSTEM_MODULES_CLASS_PREFIX + (suffix++);
rn = genSystemModulesClass(sublist(moduleInfos, cf), cf, cn, out);
map.put(moduleName, cn);
generated.add(rn);
}
}
}
// generate SystemModulesMap
rn = genSystemModulesMapClass(ALL_SYSTEM_MODULES_CLASS,
defaultSystemModulesClassName,
map,
out);
generated.add(rn);
// return the resource names of the generated classes
return generated;
}
/**
* Resolves a collection of root modules, with service binding, to create
* a Configuration for the boot layer.
*/
private Configuration resolve(ModuleFinder finder, Set<String> roots) {
return Modules.newBootLayerConfiguration(finder, roots, null);
}
/**
* Returns the list of ModuleInfo objects that correspond to the modules in
* the given configuration.
*/
private List<ModuleInfo> sublist(List<ModuleInfo> moduleInfos, Configuration cf) {
Set<String> names = cf.modules()
.stream()
.map(ResolvedModule::name)
.collect(Collectors.toSet());
return moduleInfos.stream()
.filter(mi -> names.contains(mi.moduleName()))
.toList();
}
/**
* Generate a SystemModules implementation class and add it as a resource.
*
* @return the name of the class resource added to the pool
*/
private String genSystemModulesClass(List<ModuleInfo> moduleInfos,
Configuration cf,
String className,
ResourcePoolBuilder out) {
SystemModulesClassGenerator generator
= new SystemModulesClassGenerator(className, moduleInfos);
byte[] bytes = generator.getClassWriter(cf).toByteArray();
String rn = "/java.base/" + className + ".class";
ResourcePoolEntry e = ResourcePoolEntry.create(rn, bytes);
out.add(e);
return rn;
}
static class ModuleInfo {
private final ByteArrayInputStream bais;
private final Attributes attrs;
private final Set<String> packages;
private final boolean addModulePackages;
private ModuleDescriptor descriptor; // may be different that the original one
ModuleInfo(byte[] bytes, Set<String> packages) throws IOException {
this.bais = new ByteArrayInputStream(bytes);
this.packages = packages;
this.attrs = jdk.internal.module.ModuleInfo.read(bais, null);
// If ModulePackages attribute is present, the packages from this
// module descriptor returns the packages in that attribute.
// If it's not present, ModuleDescriptor::packages only contains
// the exported and open packages from module-info.class
this.descriptor = attrs.descriptor();
if (descriptor.isAutomatic()) {
throw new InternalError("linking automatic module is not supported");
}
// add ModulePackages attribute if this module contains some packages
// and ModulePackages is not present
this.addModulePackages = packages.size() > 0 && !hasModulePackages();
}
String moduleName() {
return attrs.descriptor().name();
}
ModuleDescriptor descriptor() {
return descriptor;
}
Set<String> packages() {
return packages;
}
ModuleTarget target() {
return attrs.target();
}
ModuleHashes recordedHashes() {
return attrs.recordedHashes();
}
ModuleResolution moduleResolution() {
return attrs.moduleResolution();
}
/**
* Validates names in ModuleDescriptor
*/
void validateNames() {
Checks.requireModuleName(descriptor.name());
for (Requires req : descriptor.requires()) {
Checks.requireModuleName(req.name());
}
for (Exports e : descriptor.exports()) {
Checks.requirePackageName(e.source());
if (e.isQualified())
e.targets().forEach(Checks::requireModuleName);
}
for (Opens opens : descriptor.opens()) {
Checks.requirePackageName(opens.source());
if (opens.isQualified())
opens.targets().forEach(Checks::requireModuleName);
}
for (Provides provides : descriptor.provides()) {
Checks.requireServiceTypeName(provides.service());
provides.providers().forEach(Checks::requireServiceProviderName);
}
for (String service : descriptor.uses()) {
Checks.requireServiceTypeName(service);
}
for (String pn : descriptor.packages()) {
Checks.requirePackageName(pn);
}
for (String pn : packages) {
Checks.requirePackageName(pn);
}
}
/**
* Validates if exported and open packages are present
*/
void validatePackages() {
Set<String> nonExistPackages = new TreeSet<>();
descriptor.exports().stream()
.map(Exports::source)
.filter(pn -> !packages.contains(pn))
.forEach(nonExistPackages::add);
descriptor.opens().stream()
.map(Opens::source)
.filter(pn -> !packages.contains(pn))
.forEach(nonExistPackages::add);
if (!nonExistPackages.isEmpty()) {
throw new PluginException("Packages that are exported or open in "
+ descriptor.name() + " are not present: " + nonExistPackages);
}
}
boolean hasModulePackages() throws IOException {
Set<String> packages = new HashSet<>();
ClassVisitor cv = new ClassVisitor(Opcodes.ASM7) {
@Override
public ModuleVisitor visitModule(String name,
int flags,
String version) {
return new ModuleVisitor(Opcodes.ASM7) {
@Override
public void visitPackage(String pn) {
packages.add(pn);
}
};
}
};
try (InputStream in = getInputStream()) {
// parse module-info.class
ClassReader cr = new ClassReader(in);
cr.accept(cv, 0);
return packages.size() > 0;
}
}
/**
* Returns true if module-info.class should be rewritten to add the
* ModulePackages attribute.
*/
boolean shouldRewrite() {
return addModulePackages;
}
/**
* Returns the bytes for the (possibly updated) module-info.class.
*/
byte[] getBytes() throws IOException {
try (InputStream in = getInputStream()) {
if (shouldRewrite()) {
ModuleInfoRewriter rewriter = new ModuleInfoRewriter(in);
if (addModulePackages) {
rewriter.addModulePackages(packages);
}
// rewritten module descriptor
byte[] bytes = rewriter.getBytes();
try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) {
this.descriptor = ModuleDescriptor.read(bais);
}
return bytes;
} else {
return in.readAllBytes();
}
}
}
/*
* Returns the input stream of the module-info.class
*/
InputStream getInputStream() {
bais.reset();
return bais;
}
class ModuleInfoRewriter extends ByteArrayOutputStream {
final ModuleInfoExtender extender;
ModuleInfoRewriter(InputStream in) {
this.extender = ModuleInfoExtender.newExtender(in);
}
void addModulePackages(Set<String> packages) {
// Add ModulePackages attribute
if (packages.size() > 0) {
extender.packages(packages);
}
}
byte[] getBytes() throws IOException {
extender.write(this);
return buf;
}
}
}
/**
* Generates a SystemModules class to reconstitute the ModuleDescriptor
* and other attributes of system modules.
*/
static class SystemModulesClassGenerator {
private static final String MODULE_DESCRIPTOR_BUILDER =
"jdk/internal/module/Builder";
private static final String MODULE_DESCRIPTOR_ARRAY_SIGNATURE =
"[Ljava/lang/module/ModuleDescriptor;";
private static final String REQUIRES_MODIFIER_CLASSNAME =
"java/lang/module/ModuleDescriptor$Requires$Modifier";
private static final String EXPORTS_MODIFIER_CLASSNAME =
"java/lang/module/ModuleDescriptor$Exports$Modifier";
private static final String OPENS_MODIFIER_CLASSNAME =
"java/lang/module/ModuleDescriptor$Opens$Modifier";
private static final String MODULE_TARGET_CLASSNAME =
"jdk/internal/module/ModuleTarget";
private static final String MODULE_TARGET_ARRAY_SIGNATURE =
"[Ljdk/internal/module/ModuleTarget;";
private static final String MODULE_HASHES_ARRAY_SIGNATURE =
"[Ljdk/internal/module/ModuleHashes;";
private static final String MODULE_RESOLUTION_CLASSNAME =
"jdk/internal/module/ModuleResolution";
private static final String MODULE_RESOLUTIONS_ARRAY_SIGNATURE =
"[Ljdk/internal/module/ModuleResolution;";
private static final int MAX_LOCAL_VARS = 256;
private final int BUILDER_VAR = 0;
private final int MD_VAR = 1; // variable for ModuleDescriptor
private final int MT_VAR = 1; // variable for ModuleTarget
private final int MH_VAR = 1; // variable for ModuleHashes
private int nextLocalVar = 2; // index to next local variable
// Method visitor for generating the SystemModules::modules() method
private MethodVisitor mv;
// name of class to generate
private final String className;
// list of all ModuleDescriptorBuilders, invoked in turn when building.
private final List<ModuleInfo> moduleInfos;
// A builder to create one single Set instance for a given set of
// names or modifiers to reduce the footprint
// e.g. target modules of qualified exports
private final DedupSetBuilder dedupSetBuilder
= new DedupSetBuilder(this::getNextLocalVar);
public SystemModulesClassGenerator(String className,
List<ModuleInfo> moduleInfos) {
this.className = className;
this.moduleInfos = moduleInfos;
moduleInfos.forEach(mi -> dedups(mi.descriptor()));
}
private int getNextLocalVar() {
return nextLocalVar++;
}
/*
* Adds the given ModuleDescriptor to the system module list.
* It performs link-time validation and prepares mapping from various
* Sets to SetBuilders to emit an optimized number of sets during build.
*/
private void dedups(ModuleDescriptor md) {
// exports
for (Exports e : md.exports()) {
dedupSetBuilder.stringSet(e.targets());
dedupSetBuilder.exportsModifiers(e.modifiers());
}
// opens
for (Opens opens : md.opens()) {
dedupSetBuilder.stringSet(opens.targets());
dedupSetBuilder.opensModifiers(opens.modifiers());
}
// requires
for (Requires r : md.requires()) {
dedupSetBuilder.requiresModifiers(r.modifiers());
}
// uses
dedupSetBuilder.stringSet(md.uses());
}
/**
* Generate SystemModules class
*/
public ClassWriter getClassWriter(Configuration cf) {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
+ ClassWriter.COMPUTE_FRAMES);
cw.visit(Opcodes.V1_8,
ACC_FINAL+ACC_SUPER,
className,
null,
"java/lang/Object",
new String[] { "jdk/internal/module/SystemModules" });
// generate <init>
genConstructor(cw);
// generate hasSplitPackages
genHasSplitPackages(cw);
// generate hasIncubatorModules
genIncubatorModules(cw);
// generate moduleDescriptors
genModuleDescriptorsMethod(cw);
// generate moduleTargets
genModuleTargetsMethod(cw);
// generate moduleHashes
genModuleHashesMethod(cw);
// generate moduleResolutions
genModuleResolutionsMethod(cw);
// generate moduleReads
genModuleReads(cw, cf);
return cw;
}
/**
* Generate byteccode for no-arg constructor
*/
private void genConstructor(ClassWriter cw) {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL,
"java/lang/Object",
"<init>",
"()V",
false);
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
/**
* Generate bytecode for hasSplitPackages method
*/
private void genHasSplitPackages(ClassWriter cw) {
boolean distinct = moduleInfos.stream()
.map(ModuleInfo::packages)
.flatMap(Set::stream)
.allMatch(new HashSet<>()::add);
boolean hasSplitPackages = !distinct;
mv = cw.visitMethod(ACC_PUBLIC,
"hasSplitPackages",
"()Z",
"()Z",
null);
mv.visitCode();
if (hasSplitPackages) {
mv.visitInsn(ICONST_1);
} else {
mv.visitInsn(ICONST_0);
}
mv.visitInsn(IRETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
/**
* Generate bytecode for hasIncubatorModules method
*/
private void genIncubatorModules(ClassWriter cw) {
boolean hasIncubatorModules = moduleInfos.stream()
.map(ModuleInfo::moduleResolution)
.filter(mres -> (mres != null && mres.hasIncubatingWarning()))
.findFirst()
.isPresent();
mv = cw.visitMethod(ACC_PUBLIC,
"hasIncubatorModules",
"()Z",
"()Z",
null);
mv.visitCode();
if (hasIncubatorModules) {
mv.visitInsn(ICONST_1);
} else {
mv.visitInsn(ICONST_0);
}
mv.visitInsn(IRETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
/**
* Generate bytecode for moduleDescriptors method
*/
private void genModuleDescriptorsMethod(ClassWriter cw) {
this.mv = cw.visitMethod(ACC_PUBLIC,
"moduleDescriptors",
"()" + MODULE_DESCRIPTOR_ARRAY_SIGNATURE,
"()" + MODULE_DESCRIPTOR_ARRAY_SIGNATURE,
null);
mv.visitCode();
pushInt(mv, moduleInfos.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor");
mv.visitVarInsn(ASTORE, MD_VAR);
for (int index = 0; index < moduleInfos.size(); index++) {
ModuleInfo minfo = moduleInfos.get(index);
new ModuleDescriptorBuilder(minfo.descriptor(),
minfo.packages(),
index).build();
}
mv.visitVarInsn(ALOAD, MD_VAR);
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
/**
* Generate bytecode for moduleTargets method
*/
private void genModuleTargetsMethod(ClassWriter cw) {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC,
"moduleTargets",
"()" + MODULE_TARGET_ARRAY_SIGNATURE,
"()" + MODULE_TARGET_ARRAY_SIGNATURE,
null);
mv.visitCode();
pushInt(mv, moduleInfos.size());
mv.visitTypeInsn(ANEWARRAY, MODULE_TARGET_CLASSNAME);
mv.visitVarInsn(ASTORE, MT_VAR);
// if java.base has a ModuleTarget attribute then generate the array
// with one element, all other elements will be null.
ModuleInfo base = moduleInfos.get(0);
if (!base.moduleName().equals("java.base"))
throw new InternalError("java.base should be first module in list");
ModuleTarget target = base.target();
int count;
if (target != null && target.targetPlatform() != null) {
count = 1;
} else {
count = moduleInfos.size();
}
for (int index = 0; index < count; index++) {
ModuleInfo minfo = moduleInfos.get(index);
if (minfo.target() != null) {
mv.visitVarInsn(ALOAD, MT_VAR);
pushInt(mv, index);
// new ModuleTarget(String)
mv.visitTypeInsn(NEW, MODULE_TARGET_CLASSNAME);
mv.visitInsn(DUP);
mv.visitLdcInsn(minfo.target().targetPlatform());
mv.visitMethodInsn(INVOKESPECIAL, MODULE_TARGET_CLASSNAME,
"<init>", "(Ljava/lang/String;)V", false);
mv.visitInsn(AASTORE);
}
}
mv.visitVarInsn(ALOAD, MT_VAR);
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
/**
* Generate bytecode for moduleHashes method
*/
private void genModuleHashesMethod(ClassWriter cw) {
MethodVisitor hmv =
cw.visitMethod(ACC_PUBLIC,
"moduleHashes",
"()" + MODULE_HASHES_ARRAY_SIGNATURE,
"()" + MODULE_HASHES_ARRAY_SIGNATURE,
null);
hmv.visitCode();
pushInt(hmv, moduleInfos.size());
hmv.visitTypeInsn(ANEWARRAY, "jdk/internal/module/ModuleHashes");
hmv.visitVarInsn(ASTORE, MH_VAR);
for (int index = 0; index < moduleInfos.size(); index++) {
ModuleInfo minfo = moduleInfos.get(index);
if (minfo.recordedHashes() != null) {
new ModuleHashesBuilder(minfo.recordedHashes(),
index,
hmv).build();
}
}
hmv.visitVarInsn(ALOAD, MH_VAR);
hmv.visitInsn(ARETURN);
hmv.visitMaxs(0, 0);
hmv.visitEnd();
}
/**
* Generate bytecode for moduleResolutions method
*/
private void genModuleResolutionsMethod(ClassWriter cw) {
MethodVisitor mresmv =
cw.visitMethod(ACC_PUBLIC,
"moduleResolutions",
"()" + MODULE_RESOLUTIONS_ARRAY_SIGNATURE,
"()" + MODULE_RESOLUTIONS_ARRAY_SIGNATURE,
null);
mresmv.visitCode();
pushInt(mresmv, moduleInfos.size());
mresmv.visitTypeInsn(ANEWARRAY, MODULE_RESOLUTION_CLASSNAME);
mresmv.visitVarInsn(ASTORE, 0);
for (int index=0; index < moduleInfos.size(); index++) {
ModuleInfo minfo = moduleInfos.get(index);
if (minfo.moduleResolution() != null) {
mresmv.visitVarInsn(ALOAD, 0);
pushInt(mresmv, index);
mresmv.visitTypeInsn(NEW, MODULE_RESOLUTION_CLASSNAME);
mresmv.visitInsn(DUP);
mresmv.visitLdcInsn(minfo.moduleResolution().value());
mresmv.visitMethodInsn(INVOKESPECIAL,
MODULE_RESOLUTION_CLASSNAME,
"<init>",
"(I)V", false);
mresmv.visitInsn(AASTORE);
}
}
mresmv.visitVarInsn(ALOAD, 0);
mresmv.visitInsn(ARETURN);
mresmv.visitMaxs(0, 0);
mresmv.visitEnd();
}
/**
* Generate bytecode for moduleReads method
*/
private void genModuleReads(ClassWriter cw, Configuration cf) {
// module name -> names of modules that it reads
Map<String, Set<String>> map = cf.modules().stream()
.collect(Collectors.toMap(
ResolvedModule::name,
m -> m.reads().stream()
.map(ResolvedModule::name)
.collect(Collectors.toSet())));
generate(cw, "moduleReads", map, true);
}
/**
* Generate method to return {@code Map<String, Set<String>>}.
*
* If {@code dedup} is true then the values are de-duplicated.
*/
private void generate(ClassWriter cw,
String methodName,
Map<String, Set<String>> map,
boolean dedup) {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC,
methodName,
"()Ljava/util/Map;",
"()Ljava/util/Map;",
null);
mv.visitCode();
// map of Set -> local
Map<Set<String>, Integer> locals;
// generate code to create the sets that are duplicated
if (dedup) {
Collection<Set<String>> values = map.values();
Set<Set<String>> duplicateSets = values.stream()
.distinct()
.filter(s -> Collections.frequency(values, s) > 1)
.collect(Collectors.toSet());
locals = new HashMap<>();
int index = 1;
for (Set<String> s : duplicateSets) {
genImmutableSet(mv, s);
mv.visitVarInsn(ASTORE, index);
locals.put(s, index);
if (++index >= MAX_LOCAL_VARS) {
break;
}
}
} else {
locals = Map.of();
}
// new Map$Entry[size]
pushInt(mv, map.size());
mv.visitTypeInsn(ANEWARRAY, "java/util/Map$Entry");
int index = 0;
for (var e : new TreeMap<>(map).entrySet()) {
String name = e.getKey();
Set<String> s = e.getValue();
mv.visitInsn(DUP);
pushInt(mv, index);
mv.visitLdcInsn(name);
// if de-duplicated then load the local, otherwise generate code
Integer varIndex = locals.get(s);
if (varIndex == null) {
genImmutableSet(mv, s);
} else {
mv.visitVarInsn(ALOAD, varIndex);
}
String desc = "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/util/Map$Entry;";
mv.visitMethodInsn(INVOKESTATIC,
"java/util/Map",
"entry",
desc,
true);
mv.visitInsn(AASTORE);
index++;
}
// invoke Map.ofEntries(Map$Entry[])
mv.visitMethodInsn(INVOKESTATIC, "java/util/Map", "ofEntries",
"([Ljava/util/Map$Entry;)Ljava/util/Map;", true);
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
/**
* Generate code to generate an immutable set.
*/
private void genImmutableSet(MethodVisitor mv, Set<String> set) {
int size = set.size();
// use Set.of(Object[]) when there are more than 2 elements
// use Set.of(Object) or Set.of(Object, Object) when fewer
if (size > 2) {
pushInt(mv, size);
mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
int i = 0;
for (String element : sorted(set)) {
mv.visitInsn(DUP);
pushInt(mv, i);
mv.visitLdcInsn(element);
mv.visitInsn(AASTORE);
i++;
}
mv.visitMethodInsn(INVOKESTATIC,
"java/util/Set",
"of",
"([Ljava/lang/Object;)Ljava/util/Set;",
true);
} else {
StringBuilder sb = new StringBuilder("(");
for (String element : sorted(set)) {
mv.visitLdcInsn(element);
sb.append("Ljava/lang/Object;");
}
sb.append(")Ljava/util/Set;");
mv.visitMethodInsn(INVOKESTATIC,
"java/util/Set",
"of",
sb.toString(),
true);
}
}
class ModuleDescriptorBuilder {
static final String BUILDER_TYPE = "Ljdk/internal/module/Builder;";
static final String EXPORTS_TYPE =
"Ljava/lang/module/ModuleDescriptor$Exports;";
static final String OPENS_TYPE =
"Ljava/lang/module/ModuleDescriptor$Opens;";
static final String PROVIDES_TYPE =
"Ljava/lang/module/ModuleDescriptor$Provides;";
static final String REQUIRES_TYPE =
"Ljava/lang/module/ModuleDescriptor$Requires;";
// method signature for static Builder::newExports, newOpens,
// newProvides, newRequires methods
static final String EXPORTS_MODIFIER_SET_STRING_SET_SIG =
"(Ljava/util/Set;Ljava/lang/String;Ljava/util/Set;)"
+ EXPORTS_TYPE;
static final String EXPORTS_MODIFIER_SET_STRING_SIG =
"(Ljava/util/Set;Ljava/lang/String;)" + EXPORTS_TYPE;
static final String OPENS_MODIFIER_SET_STRING_SET_SIG =
"(Ljava/util/Set;Ljava/lang/String;Ljava/util/Set;)"
+ OPENS_TYPE;
static final String OPENS_MODIFIER_SET_STRING_SIG =
"(Ljava/util/Set;Ljava/lang/String;)" + OPENS_TYPE;
static final String PROVIDES_STRING_LIST_SIG =
"(Ljava/lang/String;Ljava/util/List;)" + PROVIDES_TYPE;
static final String REQUIRES_SET_STRING_SIG =
"(Ljava/util/Set;Ljava/lang/String;)" + REQUIRES_TYPE;
static final String REQUIRES_SET_STRING_STRING_SIG =
"(Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;)" + REQUIRES_TYPE;
// method signature for Builder instance methods that
// return this Builder instance
static final String EXPORTS_ARRAY_SIG =
"([" + EXPORTS_TYPE + ")" + BUILDER_TYPE;
static final String OPENS_ARRAY_SIG =
"([" + OPENS_TYPE + ")" + BUILDER_TYPE;
static final String PROVIDES_ARRAY_SIG =
"([" + PROVIDES_TYPE + ")" + BUILDER_TYPE;
static final String REQUIRES_ARRAY_SIG =
"([" + REQUIRES_TYPE + ")" + BUILDER_TYPE;
static final String SET_SIG = "(Ljava/util/Set;)" + BUILDER_TYPE;
static final String STRING_SIG = "(Ljava/lang/String;)" + BUILDER_TYPE;
static final String BOOLEAN_SIG = "(Z)" + BUILDER_TYPE;
final ModuleDescriptor md;
final Set<String> packages;
final int index;
ModuleDescriptorBuilder(ModuleDescriptor md, Set<String> packages, int index) {
if (md.isAutomatic()) {
throw new InternalError("linking automatic module is not supported");
}
this.md = md;
this.packages = packages;
this.index = index;
}
void build() {
// new jdk.internal.module.Builder
newBuilder();
// requires
requires(md.requires());
// exports
exports(md.exports());
// opens
opens(md.opens());
// uses
uses(md.uses());
// provides
provides(md.provides());
// all packages
packages(packages);
// version
md.version().ifPresent(this::version);
// main class
md.mainClass().ifPresent(this::mainClass);
putModuleDescriptor();
}
void newBuilder() {
mv.visitTypeInsn(NEW, MODULE_DESCRIPTOR_BUILDER);
mv.visitInsn(DUP);
mv.visitLdcInsn(md.name());
mv.visitMethodInsn(INVOKESPECIAL, MODULE_DESCRIPTOR_BUILDER,
"<init>", "(Ljava/lang/String;)V", false);
mv.visitVarInsn(ASTORE, BUILDER_VAR);
mv.visitVarInsn(ALOAD, BUILDER_VAR);
if (md.isOpen()) {
setModuleBit("open", true);
}
if (md.modifiers().contains(ModuleDescriptor.Modifier.SYNTHETIC)) {
setModuleBit("synthetic", true);
}
if (md.modifiers().contains(ModuleDescriptor.Modifier.MANDATED)) {
setModuleBit("mandated", true);
}
}
/*
* Invoke Builder.<methodName>(boolean value)
*/
void setModuleBit(String methodName, boolean value) {
mv.visitVarInsn(ALOAD, BUILDER_VAR);
if (value) {
mv.visitInsn(ICONST_1);
} else {
mv.visitInsn(ICONST_0);
}
mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
methodName, BOOLEAN_SIG, false);
mv.visitInsn(POP);
}
/*
* Put ModuleDescriptor into the modules array
*/
void putModuleDescriptor() {
mv.visitVarInsn(ALOAD, MD_VAR);
pushInt(mv, index);
mv.visitVarInsn(ALOAD, BUILDER_VAR);
mv.visitLdcInsn(md.hashCode());
mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
"build", "(I)Ljava/lang/module/ModuleDescriptor;",
false);
mv.visitInsn(AASTORE);
}
/*
* Call Builder::newRequires to create Requires instances and
* then pass it to the builder by calling:
* Builder.requires(Requires[])
*
*/
void requires(Set<Requires> requires) {
mv.visitVarInsn(ALOAD, BUILDER_VAR);
pushInt(mv, requires.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Requires");
int arrayIndex = 0;
for (Requires require : sorted(requires)) {
String compiledVersion = null;
if (require.compiledVersion().isPresent()) {
compiledVersion = require.compiledVersion().get().toString();
}
mv.visitInsn(DUP); // arrayref
pushInt(mv, arrayIndex++);
newRequires(require.modifiers(), require.name(), compiledVersion);
mv.visitInsn(AASTORE);
}
mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
"requires", REQUIRES_ARRAY_SIG, false);
}
/*
* Invoke Builder.newRequires(Set<Modifier> mods, String mn, String compiledVersion)
*
* Set<Modifier> mods = ...
* Builder.newRequires(mods, mn, compiledVersion);
*/
void newRequires(Set<Requires.Modifier> mods, String name, String compiledVersion) {
int varIndex = dedupSetBuilder.indexOfRequiresModifiers(mods);
mv.visitVarInsn(ALOAD, varIndex);
mv.visitLdcInsn(name);
if (compiledVersion != null) {
mv.visitLdcInsn(compiledVersion);
mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
"newRequires", REQUIRES_SET_STRING_STRING_SIG, false);
} else {
mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
"newRequires", REQUIRES_SET_STRING_SIG, false);
}
}
/*
* Call Builder::newExports to create Exports instances and
* then pass it to the builder by calling:
* Builder.exports(Exports[])
*
*/
void exports(Set<Exports> exports) {
mv.visitVarInsn(ALOAD, BUILDER_VAR);
pushInt(mv, exports.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Exports");
int arrayIndex = 0;
for (Exports export : sorted(exports)) {
mv.visitInsn(DUP); // arrayref
pushInt(mv, arrayIndex++);
newExports(export.modifiers(), export.source(), export.targets());
mv.visitInsn(AASTORE);
}
mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
"exports", EXPORTS_ARRAY_SIG, false);
}
/*
* Invoke
* Builder.newExports(Set<Exports.Modifier> ms, String pn,
* Set<String> targets)
* or
* Builder.newExports(Set<Exports.Modifier> ms, String pn)
*
* Set<String> targets = new HashSet<>();
* targets.add(t);
* :
* :
*
* Set<Modifier> mods = ...
* Builder.newExports(mods, pn, targets);
*/
void newExports(Set<Exports.Modifier> ms, String pn, Set<String> targets) {
int modifiersSetIndex = dedupSetBuilder.indexOfExportsModifiers(ms);
if (!targets.isEmpty()) {
int stringSetIndex = dedupSetBuilder.indexOfStringSet(targets);
mv.visitVarInsn(ALOAD, modifiersSetIndex);
mv.visitLdcInsn(pn);
mv.visitVarInsn(ALOAD, stringSetIndex);
mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
"newExports", EXPORTS_MODIFIER_SET_STRING_SET_SIG, false);
} else {
mv.visitVarInsn(ALOAD, modifiersSetIndex);
mv.visitLdcInsn(pn);
mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
"newExports", EXPORTS_MODIFIER_SET_STRING_SIG, false);
}
}
/**
* Call Builder::newOpens to create Opens instances and
* then pass it to the builder by calling:
* Builder.opens(Opens[])
*/
void opens(Set<Opens> opens) {
mv.visitVarInsn(ALOAD, BUILDER_VAR);
pushInt(mv, opens.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Opens");
int arrayIndex = 0;
for (Opens open : sorted(opens)) {
mv.visitInsn(DUP); // arrayref
pushInt(mv, arrayIndex++);
newOpens(open.modifiers(), open.source(), open.targets());
mv.visitInsn(AASTORE);
}
mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
"opens", OPENS_ARRAY_SIG, false);
}
/*
* Invoke
* Builder.newOpens(Set<Opens.Modifier> ms, String pn,
* Set<String> targets)
* or
* Builder.newOpens(Set<Opens.Modifier> ms, String pn)
*
* Set<String> targets = new HashSet<>();
* targets.add(t);
* :
* :
*
* Set<Modifier> mods = ...
* Builder.newOpens(mods, pn, targets);
*/
void newOpens(Set<Opens.Modifier> ms, String pn, Set<String> targets) {
int modifiersSetIndex = dedupSetBuilder.indexOfOpensModifiers(ms);
if (!targets.isEmpty()) {
int stringSetIndex = dedupSetBuilder.indexOfStringSet(targets);
mv.visitVarInsn(ALOAD, modifiersSetIndex);
mv.visitLdcInsn(pn);
mv.visitVarInsn(ALOAD, stringSetIndex);
mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
"newOpens", OPENS_MODIFIER_SET_STRING_SET_SIG, false);
} else {
mv.visitVarInsn(ALOAD, modifiersSetIndex);
mv.visitLdcInsn(pn);
mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
"newOpens", OPENS_MODIFIER_SET_STRING_SIG, false);
}
}
/*
* Invoke Builder.uses(Set<String> uses)
*/
void uses(Set<String> uses) {
int varIndex = dedupSetBuilder.indexOfStringSet(uses);
mv.visitVarInsn(ALOAD, BUILDER_VAR);
mv.visitVarInsn(ALOAD, varIndex);
mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
"uses", SET_SIG, false);
mv.visitInsn(POP);
}
/*
* Call Builder::newProvides to create Provides instances and
* then pass it to the builder by calling:
* Builder.provides(Provides[] provides)
*
*/
void provides(Collection<Provides> provides) {
mv.visitVarInsn(ALOAD, BUILDER_VAR);
pushInt(mv, provides.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Provides");
int arrayIndex = 0;
for (Provides provide : sorted(provides)) {
mv.visitInsn(DUP); // arrayref
pushInt(mv, arrayIndex++);
newProvides(provide.service(), provide.providers());
mv.visitInsn(AASTORE);
}
mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
"provides", PROVIDES_ARRAY_SIG, false);
}
/*
* Invoke Builder.newProvides(String service, Set<String> providers)
*
* Set<String> providers = new HashSet<>();
* providers.add(impl);
* :
* :
* Builder.newProvides(service, providers);
*/
void newProvides(String service, List<String> providers) {
mv.visitLdcInsn(service);
pushInt(mv, providers.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
int arrayIndex = 0;
for (String provider : providers) {
mv.visitInsn(DUP); // arrayref
pushInt(mv, arrayIndex++);
mv.visitLdcInsn(provider);
mv.visitInsn(AASTORE);
}
mv.visitMethodInsn(INVOKESTATIC, "java/util/List",
"of", "([Ljava/lang/Object;)Ljava/util/List;", true);
mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
"newProvides", PROVIDES_STRING_LIST_SIG, false);
}
/*
* Invoke Builder.packages(String pn)
*/
void packages(Set<String> packages) {
int varIndex = dedupSetBuilder.newStringSet(packages);
mv.visitVarInsn(ALOAD, BUILDER_VAR);
mv.visitVarInsn(ALOAD, varIndex);
mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
"packages", SET_SIG, false);
mv.visitInsn(POP);
}
/*
* Invoke Builder.mainClass(String cn)
*/
void mainClass(String cn) {
mv.visitVarInsn(ALOAD, BUILDER_VAR);
mv.visitLdcInsn(cn);
mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
"mainClass", STRING_SIG, false);
mv.visitInsn(POP);
}
/*
* Invoke Builder.version(Version v);
*/
void version(Version v) {
mv.visitVarInsn(ALOAD, BUILDER_VAR);
mv.visitLdcInsn(v.toString());
mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
"version", STRING_SIG, false);
mv.visitInsn(POP);
}
void invokeBuilderMethod(String methodName, String value) {
mv.visitVarInsn(ALOAD, BUILDER_VAR);
mv.visitLdcInsn(value);
mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
methodName, STRING_SIG, false);
mv.visitInsn(POP);
}
}
class ModuleHashesBuilder {
private static final String MODULE_HASHES_BUILDER =
"jdk/internal/module/ModuleHashes$Builder";
private static final String MODULE_HASHES_BUILDER_TYPE =
"L" + MODULE_HASHES_BUILDER + ";";
static final String STRING_BYTE_ARRAY_SIG =
"(Ljava/lang/String;[B)" + MODULE_HASHES_BUILDER_TYPE;
final ModuleHashes recordedHashes;
final MethodVisitor hmv;
final int index;
ModuleHashesBuilder(ModuleHashes hashes, int index, MethodVisitor hmv) {
this.recordedHashes = hashes;
this.hmv = hmv;
this.index = index;
}
/**
* Build ModuleHashes
*/
void build() {
if (recordedHashes == null)
return;
// new jdk.internal.module.ModuleHashes.Builder
newModuleHashesBuilder();
// Invoke ModuleHashes.Builder::hashForModule
recordedHashes
.names()
.stream()
.sorted()
.forEach(mn -> hashForModule(mn, recordedHashes.hashFor(mn)));
// Put ModuleHashes into the hashes array
pushModuleHashes();
}
/*
* Create ModuleHashes.Builder instance
*/
void newModuleHashesBuilder() {
hmv.visitTypeInsn(NEW, MODULE_HASHES_BUILDER);
hmv.visitInsn(DUP);
hmv.visitLdcInsn(recordedHashes.algorithm());
pushInt(hmv, ((4 * recordedHashes.names().size()) / 3) + 1);
hmv.visitMethodInsn(INVOKESPECIAL, MODULE_HASHES_BUILDER,
"<init>", "(Ljava/lang/String;I)V", false);
hmv.visitVarInsn(ASTORE, BUILDER_VAR);
hmv.visitVarInsn(ALOAD, BUILDER_VAR);
}
/*
* Invoke ModuleHashes.Builder::build and put the returned
* ModuleHashes to the hashes array
*/
void pushModuleHashes() {
hmv.visitVarInsn(ALOAD, MH_VAR);
pushInt(hmv, index);
hmv.visitVarInsn(ALOAD, BUILDER_VAR);
hmv.visitMethodInsn(INVOKEVIRTUAL, MODULE_HASHES_BUILDER,
"build", "()Ljdk/internal/module/ModuleHashes;",
false);
hmv.visitInsn(AASTORE);
}
/*
* Invoke ModuleHashes.Builder.hashForModule(String name, byte[] hash);
*/
void hashForModule(String name, byte[] hash) {
hmv.visitVarInsn(ALOAD, BUILDER_VAR);
hmv.visitLdcInsn(name);
pushInt(hmv, hash.length);
hmv.visitIntInsn(NEWARRAY, T_BYTE);
for (int i = 0; i < hash.length; i++) {
hmv.visitInsn(DUP); // arrayref
pushInt(hmv, i);
hmv.visitIntInsn(BIPUSH, hash[i]);
hmv.visitInsn(BASTORE);
}
hmv.visitMethodInsn(INVOKEVIRTUAL, MODULE_HASHES_BUILDER,
"hashForModule", STRING_BYTE_ARRAY_SIG, false);
hmv.visitInsn(POP);
}
}
/*
* Wraps set creation, ensuring identical sets are properly deduplicated.
*/
class DedupSetBuilder {
// map Set<String> to a specialized builder to allow them to be
// deduplicated as they are requested
final Map<Set<String>, SetBuilder<String>> stringSets = new HashMap<>();
// map Set<Requires.Modifier> to a specialized builder to allow them to be
// deduplicated as they are requested
final Map<Set<Requires.Modifier>, EnumSetBuilder<Requires.Modifier>>
requiresModifiersSets = new HashMap<>();
// map Set<Exports.Modifier> to a specialized builder to allow them to be
// deduplicated as they are requested
final Map<Set<Exports.Modifier>, EnumSetBuilder<Exports.Modifier>>
exportsModifiersSets = new HashMap<>();
// map Set<Opens.Modifier> to a specialized builder to allow them to be
// deduplicated as they are requested
final Map<Set<Opens.Modifier>, EnumSetBuilder<Opens.Modifier>>
opensModifiersSets = new HashMap<>();
private final int stringSetVar;
private final int enumSetVar;
private final IntSupplier localVarSupplier;
DedupSetBuilder(IntSupplier localVarSupplier) {
this.stringSetVar = localVarSupplier.getAsInt();
this.enumSetVar = localVarSupplier.getAsInt();
this.localVarSupplier = localVarSupplier;
}
/*
* Add the given set of strings to this builder.
*/
void stringSet(Set<String> strings) {
stringSets.computeIfAbsent(strings,
s -> new SetBuilder<>(s, stringSetVar, localVarSupplier)
).increment();
}
/*
* Add the given set of Exports.Modifiers
*/
void exportsModifiers(Set<Exports.Modifier> mods) {
exportsModifiersSets.computeIfAbsent(mods, s ->
new EnumSetBuilder<>(s, EXPORTS_MODIFIER_CLASSNAME,
enumSetVar, localVarSupplier)
).increment();
}
/*
* Add the given set of Opens.Modifiers
*/
void opensModifiers(Set<Opens.Modifier> mods) {
opensModifiersSets.computeIfAbsent(mods, s ->
new EnumSetBuilder<>(s, OPENS_MODIFIER_CLASSNAME,
enumSetVar, localVarSupplier)
).increment();
}
/*
* Add the given set of Requires.Modifiers
*/
void requiresModifiers(Set<Requires.Modifier> mods) {
requiresModifiersSets.computeIfAbsent(mods, s ->
new EnumSetBuilder<>(s, REQUIRES_MODIFIER_CLASSNAME,
enumSetVar, localVarSupplier)
).increment();
}
/*
* Retrieve the index to the given set of Strings. Emit code to
* generate it when SetBuilder::build is called.
*/
int indexOfStringSet(Set<String> names) {
return stringSets.get(names).build();
}
/*
* Retrieve the index to the given set of Exports.Modifier.
* Emit code to generate it when EnumSetBuilder::build is called.
*/
int indexOfExportsModifiers(Set<Exports.Modifier> mods) {
return exportsModifiersSets.get(mods).build();
}
/**
* Retrieve the index to the given set of Opens.Modifier.
* Emit code to generate it when EnumSetBuilder::build is called.
*/
int indexOfOpensModifiers(Set<Opens.Modifier> mods) {
return opensModifiersSets.get(mods).build();
}
/*
* Retrieve the index to the given set of Requires.Modifier.
* Emit code to generate it when EnumSetBuilder::build is called.
*/
int indexOfRequiresModifiers(Set<Requires.Modifier> mods) {
return requiresModifiersSets.get(mods).build();
}
/*
* Build a new string set without any attempt to deduplicate it.
*/
int newStringSet(Set<String> names) {
int index = new SetBuilder<>(names, stringSetVar, localVarSupplier).build();
assert index == stringSetVar;
return index;
}
}
/*
* SetBuilder generates bytecode to create one single instance of Set
* for a given set of elements and assign to a local variable slot.
* When there is only one single reference to a Set<T>,
* it will reuse defaultVarIndex. For a Set with multiple references,
* it will use a new local variable retrieved from the nextLocalVar
*/
class SetBuilder<T extends Comparable<T>> {
private final Set<T> elements;
private final int defaultVarIndex;
private final IntSupplier nextLocalVar;
private int refCount;
private int localVarIndex;
SetBuilder(Set<T> elements,
int defaultVarIndex,
IntSupplier nextLocalVar) {
this.elements = elements;
this.defaultVarIndex = defaultVarIndex;
this.nextLocalVar = nextLocalVar;
}
/*
* Increments the number of references to this particular set.
*/
final void increment() {
refCount++;
}
/**
* Generate the appropriate instructions to load an object reference
* to the element onto the stack.
*/
void visitElement(T element, MethodVisitor mv) {
mv.visitLdcInsn(element);
}
/*
* Build bytecode for the Set represented by this builder,
* or get the local variable index of a previously generated set
* (in the local scope).
*
* @return local variable index of the generated set.
*/
final int build() {
int index = localVarIndex;
if (localVarIndex == 0) {
// if non-empty and more than one set reference this builder,
// emit to a unique local
index = refCount <= 1 ? defaultVarIndex
: nextLocalVar.getAsInt();
if (index < MAX_LOCAL_VARS) {
localVarIndex = index;
} else {
// overflow: disable optimization by using localVarIndex = 0
index = defaultVarIndex;
}
generateSetOf(index);
}
return index;
}
private void generateSetOf(int index) {
if (elements.size() <= 10) {
// call Set.of(e1, e2, ...)
StringBuilder sb = new StringBuilder("(");
for (T t : sorted(elements)) {
sb.append("Ljava/lang/Object;");
visitElement(t, mv);
}
sb.append(")Ljava/util/Set;");
mv.visitMethodInsn(INVOKESTATIC, "java/util/Set",
"of", sb.toString(), true);
} else {
// call Set.of(E... elements)
pushInt(mv, elements.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
int arrayIndex = 0;
for (T t : sorted(elements)) {
mv.visitInsn(DUP); // arrayref
pushInt(mv, arrayIndex);
visitElement(t, mv); // value
mv.visitInsn(AASTORE);
arrayIndex++;
}
mv.visitMethodInsn(INVOKESTATIC, "java/util/Set",
"of", "([Ljava/lang/Object;)Ljava/util/Set;", true);
}
mv.visitVarInsn(ASTORE, index);
}
}
/*
* Generates bytecode to create one single instance of EnumSet
* for a given set of modifiers and assign to a local variable slot.
*/
class EnumSetBuilder<T extends Comparable<T>> extends SetBuilder<T> {
private final String className;
EnumSetBuilder(Set<T> modifiers, String className,
int defaultVarIndex,
IntSupplier nextLocalVar) {
super(modifiers, defaultVarIndex, nextLocalVar);
this.className = className;
}
/**
* Loads an Enum field.
*/
@Override
void visitElement(T t, MethodVisitor mv) {
mv.visitFieldInsn(GETSTATIC, className, t.toString(),
"L" + className + ";");
}
}
}
/**
* Generate SystemModulesMap and add it as a resource.
*
* @return the name of the class resource added to the pool
*/
private String genSystemModulesMapClass(String allSystemModulesClassName,
String defaultSystemModulesClassName,
Map<String, String> map,
ResourcePoolBuilder out) {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
+ ClassWriter.COMPUTE_FRAMES);
cw.visit(Opcodes.V1_8,
ACC_FINAL+ACC_SUPER,
SYSTEM_MODULES_MAP_CLASS,
null,
"java/lang/Object",
null);
// <init>
MethodVisitor mv = cw.visitMethod(0, "<init>", "()V", null, null);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL,
"java/lang/Object",
"<init>",
"()V",
false);
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
// allSystemModules()
mv = cw.visitMethod(ACC_STATIC,
"allSystemModules",
"()Ljdk/internal/module/SystemModules;",
"()Ljdk/internal/module/SystemModules;",
null);
mv.visitCode();
mv.visitTypeInsn(NEW, allSystemModulesClassName);
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL,
allSystemModulesClassName,
"<init>",
"()V",
false);
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
// defaultSystemModules()
mv = cw.visitMethod(ACC_STATIC,
"defaultSystemModules",
"()Ljdk/internal/module/SystemModules;",
"()Ljdk/internal/module/SystemModules;",
null);
mv.visitCode();
mv.visitTypeInsn(NEW, defaultSystemModulesClassName);
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL,
defaultSystemModulesClassName,
"<init>",
"()V",
false);
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
// moduleNames()
mv = cw.visitMethod(ACC_STATIC,
"moduleNames",
"()[Ljava/lang/String;",
"()[Ljava/lang/String;",
null);
mv.visitCode();
pushInt(mv, map.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
int index = 0;
for (String moduleName : sorted(map.keySet())) {
mv.visitInsn(DUP); // arrayref
pushInt(mv, index);
mv.visitLdcInsn(moduleName);
mv.visitInsn(AASTORE);
index++;
}
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
// classNames()
mv = cw.visitMethod(ACC_STATIC,
"classNames",
"()[Ljava/lang/String;",
"()[Ljava/lang/String;",
null);
mv.visitCode();
pushInt(mv, map.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
index = 0;
for (String className : sorted(map.values())) {
mv.visitInsn(DUP); // arrayref
pushInt(mv, index);
mv.visitLdcInsn(className.replace('/', '.'));
mv.visitInsn(AASTORE);
index++;
}
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
// write the class file to the pool as a resource
String rn = "/java.base/" + SYSTEM_MODULES_MAP_CLASS + ".class";
ResourcePoolEntry e = ResourcePoolEntry.create(rn, cw.toByteArray());
out.add(e);
return rn;
}
/**
* Returns a sorted copy of a collection.
*
* This is useful to ensure a deterministic iteration order.
*
* @return a sorted copy of the given collection.
*/
private static <T extends Comparable<T>> List<T> sorted(Collection<T> c) {
var l = new ArrayList<T>(c);
Collections.sort(l);
return l;
}
/**
* Pushes an int constant
*/
private static void pushInt(MethodVisitor mv, int value) {
if (value <= 5) {
mv.visitInsn(ICONST_0 + value);
} else if (value < Byte.MAX_VALUE) {
mv.visitIntInsn(BIPUSH, value);
} else if (value < Short.MAX_VALUE) {
mv.visitIntInsn(SIPUSH, value);
} else {
throw new IllegalArgumentException("exceed limit: " + value);
}
}
/**
* Returns a module finder that finds all modules in the given list
*/
private static ModuleFinder finderOf(Collection<ModuleInfo> moduleInfos) {
Supplier<ModuleReader> readerSupplier = () -> null;
Map<String, ModuleReference> namesToReference = new HashMap<>();
for (ModuleInfo mi : moduleInfos) {
String name = mi.moduleName();
ModuleReference mref
= new ModuleReferenceImpl(mi.descriptor(),
URI.create("jrt:/" + name),
readerSupplier,
null,
mi.target(),
null,
null,
mi.moduleResolution());
namesToReference.put(name, mref);
}
return new ModuleFinder() {
@Override
public Optional<ModuleReference> find(String name) {
Objects.requireNonNull(name);
return Optional.ofNullable(namesToReference.get(name));
}
@Override
public Set<ModuleReference> findAll() {
return new HashSet<>(namesToReference.values());
}
};
}
}
⏎ jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java
Or download all of them as a single archive file:
File name: jdk.jlink-17.0.5-src.zip File size: 164180 bytes Release date: 2022-09-13 Download
⇒ JDK 17 jdk.jpackage.jmod - JPackage Tool
2023-08-03, ≈20🔥, 0💬
Popular Posts:
jTDS JDBC Driver Source Code Files are provided in the source package file, jtds-1.3.1-fyi.zip. You ...
Apache Commons Codec library provides implementations of common encoders and decoders such as Base64...
Apache Log4j API provides the interface that applications should code to and provides the adapter co...
JDK 11 java.xml.crypto.jmod is the JMOD file for JDK 11 XML (eXtensible Markup Language) Crypto modu...
Jetty provides an HTTP server, HTTP client, and javax.servlet container. These components are open s...