From 25e7ac226a3be9c064c0a65c398a8165596150f7 Mon Sep 17 00:00:00 2001 From: Adam Sotona <asotona@openjdk.org> Date: Mon, 13 Mar 2023 10:13:45 +0000 Subject: [PATCH] 8294966: Convert jdk.jartool/sun.tools.jar.FingerPrint to use the ClassFile API to parse JAR entries Reviewed-by: mchung --- make/modules/jdk.jartool/Java.gmk | 2 + src/java.base/share/classes/module-info.java | 9 +- .../share/classes/module-info.java | 3 + .../classes/sun/tools/jar/FingerPrint.java | 144 +++++++++--------- 4 files changed, 83 insertions(+), 75 deletions(-) diff --git a/make/modules/jdk.jartool/Java.gmk b/make/modules/jdk.jartool/Java.gmk index 1cf56a317e7..016c2bc0c9f 100644 --- a/make/modules/jdk.jartool/Java.gmk +++ b/make/modules/jdk.jartool/Java.gmk @@ -25,3 +25,5 @@ DISABLED_WARNINGS_java += missing-explicit-ctor JAVAC_FLAGS += -XDstringConcat=inline +JAVAC_FLAGS += --enable-preview +DISABLED_WARNINGS_java += preview diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java index 8aab97eb31c..8956cb33219 100644 --- a/src/java.base/share/classes/module-info.java +++ b/src/java.base/share/classes/module-info.java @@ -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; diff --git a/src/jdk.jartool/share/classes/module-info.java b/src/jdk.jartool/share/classes/module-info.java index fc630c3002c..00a4f4df218 100644 --- a/src/jdk.jartool/share/classes/module-info.java +++ b/src/jdk.jartool/share/classes/module-info.java @@ -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; diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/FingerPrint.java b/src/jdk.jartool/share/classes/sun/tools/jar/FingerPrint.java index 3c9757874af..fb507d9846b 100644 --- a/src/jdk.jartool/share/classes/sun/tools/jar/FingerPrint.java +++ b/src/jdk.jartool/share/classes/sun/tools/jar/FingerPrint.java @@ -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