-
CSR
-
Resolution: Unresolved
-
P4
-
None
-
source
-
minimal
-
Addition of new methods in a sealed hierarchy has minimal compatibility risk.
-
Java API
-
SE
Summary
Add a few new methods to PoolEntry
classes that have nominal descriptor representations, such as ClassEntry
as ClassDesc
and Utf8Entry
as ClassDesc
or MethodTypeDesc
.
Problem
The use of Constant Pool entries are represented by nominal descriptors from the java.lang.constant
package (and String
). However, currently, there is no simple way to check if a desired nominal descriptor is represented by the constant pool entry. They must be converted to some sort of string representation by users before they are checked against constant pool entries.
This problem is exacerbated by the fact that many constant pool entries have a multitude of string representations, or their string representation is confusing in concept. For example, ClassEntry
is represented by a backing Utf8Entry
that holds a class or interface internal binary name or an array type descriptor string. Such a string is difficult to obtain when users have a ClassDesc
, which defeats the goal of using nominal descriptors to pass validated information around. What's worse, users may pass the wrong strings: they may not realize PackageEntry
stores internal binary names while ModuleEntry
stores the literal module names with no .
to /
conversion, and may have written wrong checks.
A workaround currently available is to call poolEntry.asSymbol().equals(target)
. However, this approach has two issues:
- The symbol conversion of Constant Pool entries are costly; the goal of ClassFile API is to perform minimal processing, and symbol comparisons often don't need this much processing, especially for workloads that detect whole jars or classpaths for certain class dependencies, such as patching calls to
MethodHandles.Lookup
. - The symbol conversion process may fail if a
class
file has an entry that points to aUtf8Entry
that carries invalid data; it is not convenient to catchIllegalArgumentException
in such cases.
For example, currently, to detect if a field access is System.out
, we check against a FieldInstruction fi
like:
fi.owner().name().equalsString("java/lang/System") && fi.name().equalsString("out") && fi.type().equalsString("Ljava/io/PrintStream;")
which requires raw string comparisons for performance concerns.
Solution
To facilitate good programming habits with the ClassFile API, this RFE adds extra comparison methods equalsSymbol
(and StringEntry::equalsString
):
- ClassEntry::equalsSymbol(ClassDesc)
- MethodTypeEntry::equalsSymbol(MethodTypeDesc)
- ModuleEntry::equalsSymbol(ModuleDesc)
- PackageEntry::equalsSymbol(PackageDesc)
- StringEntry::equalsString(String)
- Utf8Entry::equalsSymbol(ClassDesc)
- Utf8Entry::equalsSymbol(MethodTypeDesc)
And notes on the asSymbol
methods that the equalsSymbol
methods are better if simple equality comparisons are desired.
These new methods are at least as efficient as calling equalsString
on the backing UTF8 entries with the right strings. In addition, the special rules of some strings, such as the different leading characters for different kinds of field descriptors, can speed up checks even more. They will also not fail when the backing UTF8 entry represents malformed strings, but will simply return false
, like AccessFlags::has
currently in the ClassFile API.
For example, with the new APIs, the previous check for System.out
field access can be:
fi.owner().equalsSymbol(CD_System) && fi.name().equalsString("out") && fi.type().equalsSymbol(CD_PrintStream)
and users no longer need to worry if a particular comparison requires an internal name or a field descriptor string.
Specification
diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/ClassEntry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/ClassEntry.java
index 5017838d68e..70c370ef104 100644
--- a/src/java.base/share/classes/java/lang/classfile/constantpool/ClassEntry.java
+++ b/src/java.base/share/classes/java/lang/classfile/constantpool/ClassEntry.java
@@ -104,8 +104,22 @@ default ConstantDesc constantValue() {
* returned descriptor is never {@linkplain ClassDesc#isPrimitive()
* primitive}.
*
+ * @apiNote
+ * If only symbol equivalence is desired, {@link #equalsSymbol(ClassDesc)
+ * equalsSymbol} should be used. It requires reduced parsing and can
+ * improve {@code class} file reading performance.
+ *
* @see ConstantPoolBuilder#classEntry(ClassDesc)
* ConstantPoolBuilder::classEntry(ClassDesc)
*/
ClassDesc asSymbol();
+
+ /**
+ * {@return whether this entry describes the given reference type} Returns
+ * {@code false} if {@code desc} is primitive.
+ *
+ * @param desc the reference type
+ * @since 25
+ */
+ boolean equalsSymbol(ClassDesc desc);
}
diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/MethodTypeEntry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/MethodTypeEntry.java
index 5da36502678..f69c0953fc5 100644
--- a/src/java.base/share/classes/java/lang/classfile/constantpool/MethodTypeEntry.java
+++ b/src/java.base/share/classes/java/lang/classfile/constantpool/MethodTypeEntry.java
@@ -70,6 +70,19 @@ default ConstantDesc constantValue() {
/**
* {@return a symbolic descriptor for the {@linkplain #descriptor() method
* type}}
+ *
+ * @apiNote
+ * If only symbol equivalence is desired, {@link #equalsSymbol(MethodTypeDesc)
+ * equalsSymbol} should be used. It requires reduced parsing and can
+ * improve {@code class} file reading performance.
*/
MethodTypeDesc asSymbol();
+
+ /**
+ * {@return whether this entry describes the given method type}
+ *
+ * @param desc the method type descriptor
+ * @since 25
+ */
+ boolean equalsSymbol(MethodTypeDesc desc);
}
diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/ModuleEntry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/ModuleEntry.java
index fd920aa1231..87a45fc3775 100644
--- a/src/java.base/share/classes/java/lang/classfile/constantpool/ModuleEntry.java
+++ b/src/java.base/share/classes/java/lang/classfile/constantpool/ModuleEntry.java
@@ -55,6 +55,19 @@ public sealed interface ModuleEntry extends PoolEntry
/**
* {@return a symbolic descriptor for the {@linkplain #name() module name}}
+ *
+ * @apiNote
+ * If only symbol equivalence is desired, {@link #equalsSymbol(ModuleDesc)
+ * equalsSymbol} should be used. It requires reduced parsing and can
+ * improve {@code class} file reading performance.
*/
ModuleDesc asSymbol();
+
+ /**
+ * {@return whether this entry describes the given module}
+ *
+ * @param desc the module descriptor
+ * @since 25
+ */
+ boolean equalsSymbol(ModuleDesc desc);
}
diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/PackageEntry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/PackageEntry.java
index ec56d0a4870..9e8561c4a20 100644
--- a/src/java.base/share/classes/java/lang/classfile/constantpool/PackageEntry.java
+++ b/src/java.base/share/classes/java/lang/classfile/constantpool/PackageEntry.java
@@ -58,6 +58,19 @@ public sealed interface PackageEntry extends PoolEntry
/**
* {@return a symbolic descriptor for the {@linkplain #name() package name}}
+ *
+ * @apiNote
+ * If only symbol equivalence is desired, {@link #equalsSymbol(PackageDesc)
+ * equalsSymbol} should be used. It requires reduced parsing and can
+ * improve {@code class} file reading performance.
*/
PackageDesc asSymbol();
+
+ /**
+ * {@return whether this entry describes the given package}
+ *
+ * @param desc the package descriptor
+ * @since 25
+ */
+ boolean equalsSymbol(PackageDesc desc);
}
diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/StringEntry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/StringEntry.java
index 8a0bbb4b015..b49e74df404 100644
--- a/src/java.base/share/classes/java/lang/classfile/constantpool/StringEntry.java
+++ b/src/java.base/share/classes/java/lang/classfile/constantpool/StringEntry.java
@@ -56,7 +56,22 @@ public sealed interface StringEntry
/**
* {@return the string value for this entry}
*
+ * @apiNote
+ * A {@code Utf8Entry} can be used directly as a {@link CharSequence} if
+ * {@code String} functionalities are not strictly desired. If only string
+ * equivalence is desired, {@link #equalsString(String) equalsString} should
+ * be used. Reduction of string processing can significantly improve {@code
+ * class} file reading performance.
+ *
* @see ConstantPoolBuilder#stringEntry(String)
*/
String stringValue();
+
+ /**
+ * {@return whether this entry describes the same string as the provided string}
+ *
+ * @param value the string to compare to
+ * @since 25
+ */
+ boolean equalsString(String value);
}
diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/Utf8Entry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/Utf8Entry.java
index 1d885051b2b..549c0b79fd7 100644
--- a/src/java.base/share/classes/java/lang/classfile/constantpool/Utf8Entry.java
+++ b/src/java.base/share/classes/java/lang/classfile/constantpool/Utf8Entry.java
@@ -84,4 +84,22 @@ public sealed interface Utf8Entry
* @param s the string to compare to
*/
boolean equalsString(String s);
+
+ /**
+ * {@return whether this entry describes the descriptor string of this
+ * field type}
+ *
+ * @param desc the field type
+ * @since 25
+ */
+ boolean equalsSymbol(ClassDesc desc);
+
+ /**
+ * {@return whether this entry describes the descriptor string of this
+ * method type}
+ *
+ * @param desc the method type
+ * @since 25
+ */
+ boolean equalsSymbol(MethodTypeDesc desc);
}
- csr of
-
JDK-8342206 Convenience method to check if a constant pool entry matches nominal descriptors
-
- Open
-