8294966: Convert jdk.jartool/sun.tools.jar.FingerPrint to use the ClassFile API to parse JAR entries
Reviewed-by: mchung
This commit is contained in:
parent
3018b4705e
commit
25e7ac226a
@ -25,3 +25,5 @@
|
||||
|
||||
DISABLED_WARNINGS_java += missing-explicit-ctor
|
||||
JAVAC_FLAGS += -XDstringConcat=inline
|
||||
JAVAC_FLAGS += --enable-preview
|
||||
DISABLED_WARNINGS_java += preview
|
||||
|
@ -152,6 +152,7 @@ module java.base {
|
||||
jdk.compiler,
|
||||
jdk.incubator.concurrent, // participates in preview features
|
||||
jdk.incubator.vector, // participates in preview features
|
||||
jdk.jartool, // participates in preview features
|
||||
jdk.jdi,
|
||||
jdk.jfr,
|
||||
jdk.jshell,
|
||||
@ -191,9 +192,13 @@ module java.base {
|
||||
exports jdk.internal.logger to
|
||||
java.logging;
|
||||
exports jdk.internal.classfile to
|
||||
jdk.jlink;
|
||||
exports jdk.internal.org.objectweb.asm to
|
||||
jdk.jartool,
|
||||
jdk.jlink;
|
||||
exports jdk.internal.classfile.attribute to
|
||||
jdk.jartool;
|
||||
exports jdk.internal.classfile.constantpool to
|
||||
jdk.jartool;
|
||||
exports jdk.internal.org.objectweb.asm to
|
||||
jdk.jfr,
|
||||
jdk.jlink,
|
||||
jdk.jshell;
|
||||
|
@ -23,6 +23,8 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import jdk.internal.javac.ParticipatesInPreview;
|
||||
|
||||
/**
|
||||
* Defines tools for manipulating Java Archive (JAR) files,
|
||||
* including the <em>{@index jar jar tool}</em> and
|
||||
@ -47,6 +49,7 @@
|
||||
* @moduleGraph
|
||||
* @since 9
|
||||
*/
|
||||
@ParticipatesInPreview
|
||||
module jdk.jartool {
|
||||
requires jdk.internal.opt;
|
||||
|
||||
|
@ -25,16 +25,22 @@
|
||||
|
||||
package sun.tools.jar;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.AccessFlag;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import jdk.internal.classfile.AccessFlags;
|
||||
import jdk.internal.classfile.Attributes;
|
||||
import jdk.internal.classfile.ClassElement;
|
||||
import jdk.internal.classfile.Classfile;
|
||||
import jdk.internal.classfile.constantpool.*;
|
||||
import jdk.internal.classfile.FieldModel;
|
||||
import jdk.internal.classfile.MethodModel;
|
||||
import jdk.internal.classfile.attribute.EnclosingMethodAttribute;
|
||||
import jdk.internal.classfile.attribute.InnerClassesAttribute;
|
||||
|
||||
/**
|
||||
* A FingerPrint is an abstract representation of a JarFile entry that contains
|
||||
@ -91,7 +97,7 @@ final class FingerPrint {
|
||||
}
|
||||
|
||||
public boolean isNestedClass() {
|
||||
return attrs.nestedClass;
|
||||
return attrs.maybeNestedClass && attrs.outerClassName != null;
|
||||
}
|
||||
|
||||
public boolean isPublicClass() {
|
||||
@ -159,10 +165,14 @@ final class FingerPrint {
|
||||
return true;
|
||||
}
|
||||
|
||||
private ClassAttributes getClassAttributes(byte[] bytes) {
|
||||
ClassReader rdr = new ClassReader(bytes);
|
||||
ClassAttributes attrs = new ClassAttributes();
|
||||
rdr.accept(attrs, 0);
|
||||
private static ClassAttributes getClassAttributes(byte[] bytes) {
|
||||
var cm = Classfile.parse(bytes);
|
||||
ClassAttributes attrs = new ClassAttributes(
|
||||
cm.flags(),
|
||||
cm.thisClass().asInternalName(),
|
||||
cm.superclass().map(ClassEntry::asInternalName).orElse(null),
|
||||
cm.majorVersion());
|
||||
cm.forEachElement(attrs);
|
||||
return attrs;
|
||||
}
|
||||
|
||||
@ -232,85 +242,73 @@ final class FingerPrint {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ClassAttributes extends ClassVisitor {
|
||||
private String name;
|
||||
private static final class ClassAttributes implements Consumer<ClassElement> {
|
||||
private final String name;
|
||||
private String outerClassName;
|
||||
private String superName;
|
||||
private int majorVersion;
|
||||
private int access;
|
||||
private boolean publicClass;
|
||||
private boolean nestedClass;
|
||||
private final String superName;
|
||||
private final int majorVersion;
|
||||
private final int access;
|
||||
private final boolean publicClass;
|
||||
private final boolean maybeNestedClass;
|
||||
private final Set<Field> fields = new HashSet<>();
|
||||
private final Set<Method> methods = new HashSet<>();
|
||||
|
||||
public ClassAttributes() {
|
||||
super(Opcodes.ASM9);
|
||||
}
|
||||
|
||||
private boolean isPublic(int access) {
|
||||
return ((access & Opcodes.ACC_PUBLIC) == Opcodes.ACC_PUBLIC)
|
||||
|| ((access & Opcodes.ACC_PROTECTED) == Opcodes.ACC_PROTECTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature,
|
||||
String superName, String[] interfaces) {
|
||||
this.majorVersion = version & 0xFFFF; // JDK-8296329: extract major version only
|
||||
this.access = access;
|
||||
public ClassAttributes(AccessFlags access, String name, String superName, int majorVersion) {
|
||||
this.majorVersion = majorVersion; // JDK-8296329: extract major version only
|
||||
this.access = access.flagsMask();
|
||||
this.name = name;
|
||||
this.nestedClass = name.contains("$");
|
||||
this.maybeNestedClass = name.contains("$");
|
||||
this.superName = superName;
|
||||
this.publicClass = isPublic(access);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOuterClass(String owner, String name, String desc) {
|
||||
if (!this.nestedClass) return;
|
||||
this.outerClassName = owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInnerClass(String name, String outerName, String innerName,
|
||||
int access) {
|
||||
if (!this.nestedClass) return;
|
||||
if (outerName == null) return;
|
||||
if (!this.name.equals(name)) return;
|
||||
if (this.outerClassName == null) this.outerClassName = outerName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldVisitor visitField(int access, String name, String desc,
|
||||
String signature, Object value) {
|
||||
if (isPublic(access)) {
|
||||
fields.add(new Field(access, name, desc));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc,
|
||||
String signature, String[] exceptions) {
|
||||
if (isPublic(access)) {
|
||||
Set<String> exceptionSet = new HashSet<>();
|
||||
if (exceptions != null) {
|
||||
for (String e : exceptions) {
|
||||
exceptionSet.add(e);
|
||||
public void accept(ClassElement cle) {
|
||||
switch (cle) {
|
||||
case InnerClassesAttribute ica -> {
|
||||
for (var icm : ica.classes()) {
|
||||
if (this.maybeNestedClass && icm.outerClass().isPresent()
|
||||
&& this.name.equals(icm.innerClass().asInternalName())
|
||||
&& this.outerClassName == null) {
|
||||
this.outerClassName = icm.outerClass().get().asInternalName();
|
||||
}
|
||||
}
|
||||
}
|
||||
// treat type descriptor as a proxy for signature because signature
|
||||
// is usually null, need to strip off the return type though
|
||||
int n;
|
||||
if (desc != null && (n = desc.lastIndexOf(')')) != -1) {
|
||||
desc = desc.substring(0, n + 1);
|
||||
methods.add(new Method(access, name, desc, exceptionSet));
|
||||
case FieldModel fm -> {
|
||||
if (isPublic(fm.flags())) {
|
||||
fields.add(new Field(fm.flags().flagsMask(),
|
||||
fm.fieldName().stringValue(),
|
||||
fm.fieldType().stringValue()));
|
||||
}
|
||||
}
|
||||
case MethodModel mm -> {
|
||||
if (isPublic(mm.flags())) {
|
||||
Set<String> exceptionSet = new HashSet<>();
|
||||
mm.findAttribute(Attributes.EXCEPTIONS).ifPresent(ea ->
|
||||
ea.exceptions().forEach(e ->
|
||||
exceptionSet.add(e.asInternalName())));
|
||||
// treat type descriptor as a proxy for signature because signature
|
||||
// is usually null, need to strip off the return type though
|
||||
int n;
|
||||
var desc = mm.methodType().stringValue();
|
||||
if (desc != null && (n = desc.lastIndexOf(')')) != -1) {
|
||||
desc = desc.substring(0, n + 1);
|
||||
methods.add(new Method(mm.flags().flagsMask(),
|
||||
mm.methodName().stringValue(), desc, exceptionSet));
|
||||
}
|
||||
}
|
||||
}
|
||||
case EnclosingMethodAttribute ema -> {
|
||||
if (this.maybeNestedClass) {
|
||||
this.outerClassName = ema.enclosingClass().asInternalName();
|
||||
}
|
||||
}
|
||||
default -> {}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
this.nestedClass = this.outerClassName != null;
|
||||
private static boolean isPublic(AccessFlags access) {
|
||||
return access.has(AccessFlag.PUBLIC) || access.has(AccessFlag.PROTECTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Loading…
x
Reference in New Issue
Block a user