Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
8294966: Convert jdk.jartool/sun.tools.jar.FingerPrint to use the Cla…
…ssFile API to parse JAR entries

Reviewed-by: mchung
  • Loading branch information
asotona committed Mar 13, 2023
1 parent 3018b47 commit 25e7ac2
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 74 deletions.
2 changes: 2 additions & 0 deletions make/modules/jdk.jartool/Java.gmk
Expand Up @@ -25,3 +25,5 @@

DISABLED_WARNINGS_java += missing-explicit-ctor
JAVAC_FLAGS += -XDstringConcat=inline
JAVAC_FLAGS += --enable-preview
DISABLED_WARNINGS_java += preview
7 changes: 6 additions & 1 deletion src/java.base/share/classes/module-info.java
Expand Up @@ -152,6 +152,7 @@
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,
Expand Down Expand Up @@ -191,9 +192,13 @@
exports jdk.internal.logger to
java.logging;
exports jdk.internal.classfile 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.jartool,
jdk.jfr,
jdk.jlink,
jdk.jshell;
Expand Down
3 changes: 3 additions & 0 deletions src/jdk.jartool/share/classes/module-info.java
Expand Up @@ -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
Expand All @@ -47,6 +49,7 @@
* @moduleGraph
* @since 9
*/
@ParticipatesInPreview
module jdk.jartool {
requires jdk.internal.opt;

Expand Down
144 changes: 71 additions & 73 deletions src/jdk.jartool/share/classes/sun/tools/jar/FingerPrint.java
Expand Up @@ -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
Expand Down Expand Up @@ -91,7 +97,7 @@ public boolean isClass() {
}

public boolean isNestedClass() {
return attrs.nestedClass;
return attrs.maybeNestedClass && attrs.outerClassName != null;
}

public boolean isPublicClass() {
Expand Down Expand Up @@ -159,10 +165,14 @@ private boolean isCafeBabe(byte[] bytes) {
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;
}

Expand Down Expand Up @@ -232,85 +242,73 @@ public int hashCode() {
}
}

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
Expand Down

1 comment on commit 25e7ac2

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.