8294972: Convert jdk.jlink internal plugins to use the Classfile API
Reviewed-by: mchung, alanb
This commit is contained in:
parent
c74507eeb3
commit
358c61b58d
src
java.base/share/classes
jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins
@ -196,17 +196,18 @@ module java.base {
|
||||
jdk.jlink,
|
||||
jdk.jshell;
|
||||
exports jdk.internal.classfile.attribute to
|
||||
jdk.jartool;
|
||||
jdk.jartool,
|
||||
jdk.jlink;
|
||||
exports jdk.internal.classfile.constantpool to
|
||||
jdk.jartool;
|
||||
jdk.jartool,
|
||||
jdk.jlink;
|
||||
exports jdk.internal.classfile.instruction to
|
||||
jdk.jlink,
|
||||
jdk.jshell;
|
||||
exports jdk.internal.org.objectweb.asm to
|
||||
jdk.jfr,
|
||||
jdk.jlink;
|
||||
jdk.jfr;
|
||||
exports jdk.internal.org.objectweb.asm.tree to
|
||||
jdk.jfr,
|
||||
jdk.jlink;
|
||||
jdk.jfr;
|
||||
exports jdk.internal.org.objectweb.asm.util to
|
||||
jdk.jfr;
|
||||
exports jdk.internal.org.objectweb.asm.commons to
|
||||
|
@ -35,7 +35,8 @@ import java.nio.file.Paths;
|
||||
import java.util.Locale;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.ResourceBundle;
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.classfile.ClassModel;
|
||||
import jdk.internal.classfile.Classfile;
|
||||
import jdk.tools.jlink.plugin.ResourcePoolEntry;
|
||||
|
||||
public abstract class AbstractPlugin implements Plugin {
|
||||
@ -84,10 +85,10 @@ public abstract class AbstractPlugin implements Plugin {
|
||||
}
|
||||
}
|
||||
|
||||
ClassReader newClassReader(String path, ResourcePoolEntry resource) {
|
||||
ClassModel newClassReader(String path, ResourcePoolEntry resource, Classfile.Option... options) {
|
||||
byte[] content = resource.contentBytes();
|
||||
try {
|
||||
return new ClassReader(content);
|
||||
return Classfile.parse(content, options);
|
||||
} catch (Exception e) {
|
||||
if (JlinkTask.DEBUG) {
|
||||
System.err.printf("Failed to parse class file: %s from resource of type %s\n", path,
|
||||
@ -99,9 +100,9 @@ public abstract class AbstractPlugin implements Plugin {
|
||||
}
|
||||
}
|
||||
|
||||
protected ClassReader newClassReader(String path, byte[] buf) {
|
||||
protected ClassModel newClassReader(String path, byte[] buf, Classfile.Option... options) {
|
||||
try {
|
||||
return new ClassReader(buf);
|
||||
return Classfile.parse(buf, options);
|
||||
} catch (Exception e) {
|
||||
if (JlinkTask.DEBUG) {
|
||||
System.err.printf("Failed to parse class file: %s\n", path);
|
||||
|
@ -35,12 +35,12 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import static java.util.ResourceBundle.Control;
|
||||
import java.util.Set;
|
||||
import java.util.function.IntUnaryOperator;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import static jdk.internal.classfile.Classfile.*;
|
||||
import jdk.tools.jlink.internal.ResourcePrevisitor;
|
||||
import jdk.tools.jlink.internal.StringTable;
|
||||
import jdk.tools.jlink.plugin.ResourcePoolModule;
|
||||
@ -159,10 +159,9 @@ public final class IncludeLocalesPlugin extends AbstractPlugin implements Resour
|
||||
resource.type().equals(ResourcePoolEntry.Type.CLASS_OR_RESOURCE) &&
|
||||
path.endsWith(".class")) {
|
||||
byte[] bytes = resource.contentBytes();
|
||||
ClassReader cr = newClassReader(path, bytes);
|
||||
if (Arrays.stream(cr.getInterfaces())
|
||||
.anyMatch(i -> i.contains(METAINFONAME)) &&
|
||||
stripUnsupportedLocales(bytes, cr)) {
|
||||
if (newClassReader(path, bytes).interfaces().stream()
|
||||
.anyMatch(i -> i.asInternalName().contains(METAINFONAME)) &&
|
||||
stripUnsupportedLocales(bytes)) {
|
||||
resource = resource.copyWithContent(bytes);
|
||||
}
|
||||
}
|
||||
@ -270,26 +269,49 @@ public final class IncludeLocalesPlugin extends AbstractPlugin implements Resour
|
||||
.toList();
|
||||
}
|
||||
|
||||
private boolean stripUnsupportedLocales(byte[] bytes, ClassReader cr) {
|
||||
boolean[] modified = new boolean[1];
|
||||
|
||||
IntStream.range(1, cr.getItemCount())
|
||||
.map(item -> cr.getItem(item))
|
||||
.forEach(itemIndex -> {
|
||||
if (bytes[itemIndex - 1] == 1 && // UTF-8
|
||||
bytes[itemIndex + 2] == (byte)' ') { // fast check for leading space
|
||||
int length = cr.readUnsignedShort(itemIndex);
|
||||
byte[] b = new byte[length];
|
||||
System.arraycopy(bytes, itemIndex + 2, b, 0, length);
|
||||
if (filterOutUnsupportedTags(b)) {
|
||||
// copy back
|
||||
System.arraycopy(b, 0, bytes, itemIndex + 2, length);
|
||||
modified[0] = true;
|
||||
private boolean stripUnsupportedLocales(byte[] bytes) {
|
||||
boolean modified = false;
|
||||
// scan CP entries directly to read the bytes of UTF8 entries and
|
||||
// patch in place with unsupported locale tags stripped
|
||||
IntUnaryOperator readU2 = p -> ((bytes[p] & 0xff) << 8) + (bytes[p + 1] & 0xff);
|
||||
int cpLength = readU2.applyAsInt(8);
|
||||
int offset = 10;
|
||||
for (int cpSlot=1; cpSlot<cpLength; cpSlot++) {
|
||||
switch (bytes[offset]) { //entry tag
|
||||
case TAG_UTF8 -> {
|
||||
int length = readU2.applyAsInt(offset + 1);
|
||||
if (bytes[offset + 3] == (byte)' ') { // fast check for leading space
|
||||
byte[] b = new byte[length];
|
||||
System.arraycopy(bytes, offset + 3, b, 0, length);
|
||||
if (filterOutUnsupportedTags(b)) {
|
||||
// copy back
|
||||
System.arraycopy(b, 0, bytes, offset + 3, length);
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
offset += 3 + length;
|
||||
}
|
||||
});
|
||||
|
||||
return modified[0];
|
||||
case TAG_CLASS,
|
||||
TAG_STRING,
|
||||
TAG_METHODTYPE,
|
||||
TAG_MODULE,
|
||||
TAG_PACKAGE -> offset += 3;
|
||||
case TAG_METHODHANDLE -> offset += 4;
|
||||
case TAG_INTEGER,
|
||||
TAG_FLOAT,
|
||||
TAG_FIELDREF,
|
||||
TAG_METHODREF,
|
||||
TAG_INTERFACEMETHODREF,
|
||||
TAG_NAMEANDTYPE,
|
||||
TAG_CONSTANTDYNAMIC,
|
||||
TAG_INVOKEDYNAMIC -> offset += 5;
|
||||
case TAG_LONG,
|
||||
TAG_DOUBLE -> {offset += 9; cpSlot++;} //additional slot for double and long entries
|
||||
default -> throw new IllegalArgumentException("Unknown constant pool entry: 0x"
|
||||
+ Integer.toHexString(Byte.toUnsignedInt(bytes[offset])).toUpperCase(Locale.ROOT));
|
||||
}
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
private boolean filterOutUnsupportedTags(byte[] b) {
|
||||
|
@ -25,9 +25,14 @@
|
||||
package jdk.tools.jlink.internal.plugins;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
import jdk.internal.classfile.Classfile;
|
||||
import jdk.internal.classfile.ClassTransform;
|
||||
import jdk.internal.classfile.CodeTransform;
|
||||
import jdk.internal.classfile.MethodTransform;
|
||||
import jdk.internal.classfile.attribute.MethodParametersAttribute;
|
||||
import jdk.internal.classfile.attribute.SourceFileAttribute;
|
||||
import jdk.internal.classfile.attribute.SourceDebugExtensionAttribute;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.tools.jlink.plugin.ResourcePool;
|
||||
import jdk.tools.jlink.plugin.ResourcePoolBuilder;
|
||||
import jdk.tools.jlink.plugin.ResourcePoolEntry;
|
||||
@ -57,12 +62,17 @@ public final class StripJavaDebugAttributesPlugin extends AbstractPlugin {
|
||||
String path = resource.path();
|
||||
if (path.endsWith(".class")) {
|
||||
if (path.endsWith("module-info.class")) {
|
||||
// XXX. Do we have debug info? Is Asm ready for module-info?
|
||||
// XXX. Do we have debug info?
|
||||
} else {
|
||||
ClassReader reader = newClassReader(path, resource);
|
||||
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
reader.accept(writer, ClassReader.SKIP_DEBUG);
|
||||
byte[] content = writer.toByteArray();
|
||||
byte[] content = newClassReader(path, resource,
|
||||
Classfile.Option.processDebug(false),
|
||||
Classfile.Option.processLineNumbers(false)).transform(ClassTransform
|
||||
.dropping(cle -> cle instanceof SourceFileAttribute
|
||||
|| cle instanceof SourceDebugExtensionAttribute)
|
||||
.andThen(ClassTransform.transformingMethods(MethodTransform
|
||||
.dropping(me -> me instanceof MethodParametersAttribute)
|
||||
.andThen(MethodTransform
|
||||
.transformingCode(CodeTransform.ACCEPT_ALL)))));
|
||||
res = resource.copyWithContent(content);
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -26,12 +26,13 @@
|
||||
package jdk.tools.jlink.internal.plugins;
|
||||
|
||||
import java.util.Map;
|
||||
import jdk.internal.classfile.ClassTransform;
|
||||
import jdk.internal.classfile.CodeBuilder;
|
||||
import jdk.internal.classfile.CodeElement;
|
||||
import jdk.internal.classfile.Instruction;
|
||||
import jdk.internal.classfile.instruction.FieldInstruction;
|
||||
import jdk.internal.classfile.CodeTransform;
|
||||
|
||||
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.Opcodes;
|
||||
import jdk.tools.jlink.plugin.ResourcePool;
|
||||
import jdk.tools.jlink.plugin.ResourcePoolBuilder;
|
||||
import jdk.tools.jlink.plugin.ResourcePoolEntry;
|
||||
@ -96,63 +97,38 @@ abstract class VersionPropsPlugin extends AbstractPlugin {
|
||||
|
||||
private boolean redefined = false;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private byte[] redefine(String path, byte[] classFile) {
|
||||
return newClassReader(path, classFile).transform(ClassTransform.transformingMethodBodies(
|
||||
mm -> mm.methodName().equalsString("<clinit>"),
|
||||
new CodeTransform() {
|
||||
private CodeElement pendingLDC = null;
|
||||
|
||||
var cr = newClassReader(path, classFile);
|
||||
var cw = new ClassWriter(0);
|
||||
private void flushPendingLDC(CodeBuilder cob) {
|
||||
if (pendingLDC != null) {
|
||||
cob.accept(pendingLDC);
|
||||
pendingLDC = null;
|
||||
}
|
||||
}
|
||||
|
||||
cr.accept(new ClassVisitor(Opcodes.ASM7, cw) {
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access,
|
||||
String name,
|
||||
String desc,
|
||||
String sig,
|
||||
String[] xs)
|
||||
{
|
||||
if (name.equals("<clinit>"))
|
||||
return new MethodVisitor(Opcodes.ASM7,
|
||||
super.visitMethod(access,
|
||||
name,
|
||||
desc,
|
||||
sig,
|
||||
xs))
|
||||
{
|
||||
private Object pendingLDC = null;
|
||||
|
||||
private void flushPendingLDC() {
|
||||
if (pendingLDC != null) {
|
||||
super.visitLdcInsn(pendingLDC);
|
||||
pendingLDC = null;
|
||||
}
|
||||
@Override
|
||||
public void accept(CodeBuilder cob, CodeElement coe) {
|
||||
if (coe instanceof Instruction ins) {
|
||||
switch (ins.opcode()) {
|
||||
case LDC, LDC_W, LDC2_W -> {
|
||||
flushPendingLDC(cob);
|
||||
pendingLDC = coe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLdcInsn(Object value) {
|
||||
flushPendingLDC();
|
||||
pendingLDC = value;
|
||||
case INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC, INVOKEINTERFACE -> {
|
||||
flushPendingLDC(cob);
|
||||
cob.accept(coe);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodInsn(int opcode,
|
||||
String owner,
|
||||
String name,
|
||||
String descriptor,
|
||||
boolean isInterface) {
|
||||
flushPendingLDC();
|
||||
super.visitMethodInsn(opcode, owner, name,
|
||||
descriptor, isInterface);
|
||||
case GETSTATIC, GETFIELD, PUTFIELD -> {
|
||||
flushPendingLDC(cob);
|
||||
cob.accept(coe);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFieldInsn(int opcode,
|
||||
String owner,
|
||||
String name,
|
||||
String desc)
|
||||
{
|
||||
if (opcode == Opcodes.PUTSTATIC
|
||||
&& name.equals(field))
|
||||
{
|
||||
case PUTSTATIC -> {
|
||||
if (((FieldInstruction)coe).name().equalsString(field)) {
|
||||
// assert that there is a pending ldc
|
||||
// for the old value
|
||||
if (pendingLDC == null) {
|
||||
@ -164,24 +140,20 @@ abstract class VersionPropsPlugin extends AbstractPlugin {
|
||||
// forget about it
|
||||
pendingLDC = null;
|
||||
// and add an ldc for the new value
|
||||
super.visitLdcInsn(value);
|
||||
cob.constantInstruction(value);
|
||||
redefined = true;
|
||||
} else {
|
||||
flushPendingLDC();
|
||||
flushPendingLDC(cob);
|
||||
}
|
||||
super.visitFieldInsn(opcode, owner,
|
||||
name, desc);
|
||||
cob.accept(coe);
|
||||
}
|
||||
|
||||
};
|
||||
else
|
||||
return super.visitMethod(access, name, desc, sig, xs);
|
||||
}
|
||||
|
||||
}, 0);
|
||||
|
||||
return cw.toByteArray();
|
||||
|
||||
default -> cob.accept(coe);
|
||||
}
|
||||
} else {
|
||||
cob.accept(coe);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -198,5 +170,4 @@ abstract class VersionPropsPlugin extends AbstractPlugin {
|
||||
throw new AssertionError(field);
|
||||
return out.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user