|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package jdk.internal.module; |
|
|
|
import java.io.IOException; |
|
import java.io.InputStream; |
|
import java.io.OutputStream; |
|
import java.lang.module.ModuleDescriptor.Version; |
|
import java.util.ArrayList; |
|
import java.util.Collections; |
|
import java.util.List; |
|
import java.util.Set; |
|
|
|
import jdk.internal.org.objectweb.asm.Attribute; |
|
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.ModuleVisitor; |
|
import jdk.internal.org.objectweb.asm.Opcodes; |
|
import jdk.internal.org.objectweb.asm.commons.ModuleHashesAttribute; |
|
import jdk.internal.org.objectweb.asm.commons.ModuleResolutionAttribute; |
|
import jdk.internal.org.objectweb.asm.commons.ModuleTargetAttribute; |
|
|
|
/** |
|
* Utility class to extend a module-info.class with additional attributes. |
|
*/ |
|
|
|
public final class ModuleInfoExtender { |
|
|
|
|
|
private final InputStream in; |
|
|
|
|
|
private Set<String> packages; |
|
|
|
|
|
private Version version; |
|
|
|
|
|
private String mainClass; |
|
|
|
|
|
private String targetPlatform; |
|
|
|
|
|
private ModuleHashes hashes; |
|
|
|
|
|
private ModuleResolution moduleResolution; |
|
|
|
private ModuleInfoExtender(InputStream in) { |
|
this.in = in; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public ModuleInfoExtender packages(Set<String> packages) { |
|
this.packages = Collections.unmodifiableSet(packages); |
|
return this; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public ModuleInfoExtender version(Version version) { |
|
this.version = version; |
|
return this; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public ModuleInfoExtender mainClass(String mainClass) { |
|
this.mainClass = mainClass; |
|
return this; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public ModuleInfoExtender targetPlatform(String targetPlatform) { |
|
this.targetPlatform = targetPlatform; |
|
return this; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public ModuleInfoExtender hashes(ModuleHashes hashes) { |
|
this.hashes = hashes; |
|
return this; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public ModuleInfoExtender moduleResolution(ModuleResolution mres) { |
|
this.moduleResolution = mres; |
|
return this; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void write(OutputStream out) throws IOException { |
|
|
|
out.write(toByteArray()); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public byte[] toByteArray() throws IOException { |
|
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS |
|
+ ClassWriter.COMPUTE_FRAMES); |
|
|
|
ClassReader cr = new ClassReader(in); |
|
|
|
ClassVisitor cv = new ClassVisitor(Opcodes.ASM6, cw) { |
|
@Override |
|
public ModuleVisitor visitModule(String name, int flags, String version) { |
|
Version v = ModuleInfoExtender.this.version; |
|
String vs = (v != null) ? v.toString() : version; |
|
ModuleVisitor mv = super.visitModule(name, flags, vs); |
|
|
|
|
|
if (mainClass != null) { |
|
mv.visitMainClass(mainClass.replace('.', '/')); |
|
} |
|
|
|
|
|
if (packages != null) { |
|
packages.forEach(pn -> mv.visitPackage(pn.replace('.', '/'))); |
|
} |
|
|
|
return new ModuleVisitor(Opcodes.ASM6, mv) { |
|
public void visitMainClass(String existingMainClass) { |
|
|
|
if (mainClass == null) { |
|
super.visitMainClass(existingMainClass); |
|
} |
|
} |
|
public void visitPackage(String existingPackage) { |
|
|
|
if (packages == null) { |
|
super.visitPackage(existingPackage); |
|
} |
|
} |
|
}; |
|
} |
|
@Override |
|
public void visitAttribute(Attribute attr) { |
|
String name = attr.type; |
|
|
|
if (name.equals(ClassFileConstants.MODULE_TARGET) |
|
&& targetPlatform != null) |
|
return; |
|
if (name.equals(ClassFileConstants.MODULE_RESOLUTION) |
|
&& moduleResolution != null) |
|
return; |
|
if (name.equals(ClassFileConstants.MODULE_HASHES) |
|
&& hashes != null) |
|
return; |
|
|
|
super.visitAttribute(attr); |
|
|
|
} |
|
}; |
|
|
|
List<Attribute> attrs = new ArrayList<>(); |
|
attrs.add(new ModuleTargetAttribute()); |
|
attrs.add(new ModuleResolutionAttribute()); |
|
attrs.add(new ModuleHashesAttribute()); |
|
cr.accept(cv, attrs.toArray(new Attribute[0]), 0); |
|
|
|
|
|
if (targetPlatform != null) { |
|
cw.visitAttribute(new ModuleTargetAttribute(targetPlatform)); |
|
} |
|
if (moduleResolution != null) { |
|
int flags = moduleResolution.value(); |
|
cw.visitAttribute(new ModuleResolutionAttribute(flags)); |
|
} |
|
if (hashes != null) { |
|
String algorithm = hashes.algorithm(); |
|
List<String> names = new ArrayList<>(); |
|
List<byte[]> values = new ArrayList<>(); |
|
for (String name : hashes.names()) { |
|
names.add(name); |
|
values.add(hashes.hashFor(name)); |
|
} |
|
cw.visitAttribute(new ModuleHashesAttribute(algorithm, names, values)); |
|
} |
|
|
|
return cw.toByteArray(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static ModuleInfoExtender newExtender(InputStream in) { |
|
return new ModuleInfoExtender(in); |
|
} |
|
|
|
} |