Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: openjdk/jdk
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 4486f1b7
Choose a base ref
...
head repository: openjdk/jdk
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 4e5b9651
Choose a head ref
Loading
13 changes: 7 additions & 6 deletions src/java.base/share/classes/module-info.java
Original file line number Diff line number Diff line change
@@ -196,17 +196,18 @@
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
Original file line number Diff line number Diff line change
@@ -35,7 +35,8 @@
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 @@ private void dumpClassFile(String path, byte[] buf) {
}
}

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 @@ ClassReader newClassReader(String path, ResourcePoolEntry resource) {
}
}

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);
Original file line number Diff line number Diff line change
@@ -35,12 +35,12 @@
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 ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
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,47 @@ private List<String> includeLocaleFiles(String localeStr) {
.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
}
}
return modified;
}

private boolean filterOutUnsupportedTags(byte[] b) {
Original file line number Diff line number Diff line change
@@ -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 ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
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);
}
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -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 @@ public void configure(Map<String, String> config) {

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;

private void flushPendingLDC(CodeBuilder cob) {
if (pendingLDC != null) {
cob.accept(pendingLDC);
pendingLDC = null;
}
}

var cr = newClassReader(path, classFile);
var cw = new ClassWriter(0);

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 @@ public void visitFieldInsn(int opcode,
// 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 @@ public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
throw new AssertionError(field);
return out.build();
}

}