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