From 38cf849d8865581d659768d89c2c2c9c1505f2c2 Mon Sep 17 00:00:00 2001 From: liach Date: Sat, 22 Apr 2023 09:30:01 -0500 Subject: [PATCH 1/2] 8306697: Add method to obtain String for CONSTANT_Class_info in ClassDesc --- .../classes/java/lang/constant/ClassDesc.java | 18 ++++++++++++++++++ .../lang/constant/PrimitiveClassDescImpl.java | 7 ++++++- .../lang/constant/ReferenceClassDescImpl.java | 5 +++++ test/jdk/java/lang/constant/ClassDescTest.java | 18 ++++++++++++------ 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/java.base/share/classes/java/lang/constant/ClassDesc.java b/src/java.base/share/classes/java/lang/constant/ClassDesc.java index 06601c475b369..61ce0fad1f823 100644 --- a/src/java.base/share/classes/java/lang/constant/ClassDesc.java +++ b/src/java.base/share/classes/java/lang/constant/ClassDesc.java @@ -355,6 +355,24 @@ else if (isArray()) { throw new IllegalStateException(descriptorString()); } + /** + * {@return the string representation of this {@linkplain ClassDesc} in a + * {@code CONSTANT_Class_info} ({@jvms 4.4.1})} This is the binary name + * in internal form for classes and interfaces, and descriptors for arrays. + * Primitive types cannot be represented by {@code CONSTANT_Class_info} + * structures. + * + * @apiNote + * Unlike {@link #ofInternalName ofInternalName} that rejects array descriptors, + * this method returns the array descriptor string if this {@linkplain ClassDesc} + * represents an array. + * + * @throws IllegalStateException if this {@linkplain ClassDesc} describes a type + * that cannot be represented by a {@code CONSTANT_Class_info}, such as primitive types + * @since 21 + */ + String internalName(); + /** * Returns a field type descriptor string for this type * diff --git a/src/java.base/share/classes/java/lang/constant/PrimitiveClassDescImpl.java b/src/java.base/share/classes/java/lang/constant/PrimitiveClassDescImpl.java index 0478fbe3bbac3..f65326d94fb14 100644 --- a/src/java.base/share/classes/java/lang/constant/PrimitiveClassDescImpl.java +++ b/src/java.base/share/classes/java/lang/constant/PrimitiveClassDescImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -57,6 +57,11 @@ final class PrimitiveClassDescImpl this.descriptor = descriptor; } + @Override + public String internalName() { + throw new IllegalStateException("primitive type " + displayName() + " cannot be encoded in CONSTANT_Class_info"); + } + @Override public String descriptorString() { return descriptor; diff --git a/src/java.base/share/classes/java/lang/constant/ReferenceClassDescImpl.java b/src/java.base/share/classes/java/lang/constant/ReferenceClassDescImpl.java index 4cc77b1851deb..84b728ffa9fe6 100644 --- a/src/java.base/share/classes/java/lang/constant/ReferenceClassDescImpl.java +++ b/src/java.base/share/classes/java/lang/constant/ReferenceClassDescImpl.java @@ -55,6 +55,11 @@ final class ReferenceClassDescImpl implements ClassDesc { this.descriptor = descriptor; } + @Override + public String internalName() { + return isArray() ? descriptorString() : descriptorString().substring(1, descriptorString().length() - 1); + } + @Override public String descriptorString() { return descriptor; diff --git a/test/jdk/java/lang/constant/ClassDescTest.java b/test/jdk/java/lang/constant/ClassDescTest.java index 4c9493721d9c4..ee561f33b37f2 100644 --- a/test/jdk/java/lang/constant/ClassDescTest.java +++ b/test/jdk/java/lang/constant/ClassDescTest.java @@ -33,12 +33,7 @@ import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotEquals; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; +import static org.testng.Assert.*; /** * @test @@ -64,10 +59,15 @@ private void testClassDesc(ClassDesc r) throws ReflectiveOperationException { assertEquals(r, Array.newInstance(r.resolveConstantDesc(LOOKUP), 0).getClass().describeConstable().orElseThrow().componentType()); } + if (r.isClassOrInterface()) { + assertEquals(r.descriptorString(), "L" + r.internalName() + ";"); + } + if (r.isArray()) { assertEquals(r, r.componentType().arrayType()); assertEquals(r, r.resolveConstantDesc(LOOKUP).getComponentType().describeConstable().orElseThrow().arrayType()); assertEquals(r, Array.newInstance(r.componentType().resolveConstantDesc(LOOKUP), 0).getClass().describeConstable().orElseThrow()); + assertEquals(r.descriptorString(), r.internalName()); } } @@ -77,6 +77,10 @@ private void testClassDesc(ClassDesc r, Class c) throws ReflectiveOperationEx assertEquals(r.resolveConstantDesc(LOOKUP), c); assertEquals(c.describeConstable().orElseThrow(), r); assertEquals(ClassDesc.ofDescriptor(c.descriptorString()), r); + + if (!c.isPrimitive()) { + assertEquals(c.getName(), r.internalName().replace('/', '.')); + } } public void testSymbolicDescsConstants() throws ReflectiveOperationException { @@ -118,6 +122,8 @@ public void testPrimitiveClassDesc() throws ReflectiveOperationException { assertEquals(c, p.arrayClass.describeConstable().orElseThrow().componentType()); assertEquals(c, p.classDesc.arrayType().componentType()); } + + assertThrows(IllegalStateException.class, c::internalName); } for (Primitives other : Primitives.values()) { From 4620ec2e466d1d7411d6833a3d557ed97053c07d Mon Sep 17 00:00:00 2001 From: liach Date: Sat, 22 Apr 2023 13:32:40 -0500 Subject: [PATCH 2/2] Unify ofInternalName and internalName, document about CONSTANT_Class_info, remove misleading JVMS 4.4.1 links --- .../classes/java/lang/constant/ClassDesc.java | 53 +++++++++++-------- .../jdk/java/lang/constant/ClassDescTest.java | 8 ++- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/src/java.base/share/classes/java/lang/constant/ClassDesc.java b/src/java.base/share/classes/java/lang/constant/ClassDesc.java index 61ce0fad1f823..d07fff700cee4 100644 --- a/src/java.base/share/classes/java/lang/constant/ClassDesc.java +++ b/src/java.base/share/classes/java/lang/constant/ClassDesc.java @@ -50,6 +50,16 @@ * {@linkplain ClassDesc} for the component type and then call the {@link #arrayType()} * or {@link #arrayType(int)} methods. * + *

The {@code CONSTANT_Class_info} Structure

+ * + *

While most appearances of {@link Class} constants are in the form of {@linkplain + * #descriptorString() descriptors}, in the {@code CONSTANT_Class_info} structure + * (JVMS {@jvms 4.4.1}), they appear as binary name encoded in internal forms, + * such as {@code "java/lang/String"} for {@link String} class. Such forms can be + * converted to ClassDesc with {@link #ofInternalName(String)} and retrieved from a + * {@linkplain ClassDesc} with {@link #internalName()}. In addition, primitive types + * cannot be encoded in the {@code CONSTANT_Class_info} structure. + * * @see ConstantDescs * * @since 12 @@ -82,28 +92,29 @@ static ClassDesc of(String name) { } /** - * Returns a {@linkplain ClassDesc} for a class or interface type, - * given the name of the class or interface in internal form, - * such as {@code "java/lang/String"}. - * - * @apiNote - * To create a descriptor for an array type, either use {@link #ofDescriptor(String)} - * or {@link #arrayType()}; to create a descriptor for a primitive type, use - * {@link #ofDescriptor(String)} or use the predefined constants in - * {@link ConstantDescs}. + * {@return a {@linkplain ClassDesc} from a string representation + * in a {@link ##constant-class-info CONSTANT_Class_info} structure} * - * @param name the fully qualified class name, in internal (slash-separated) form - * @return a {@linkplain ClassDesc} describing the desired class + * @param name the class name, compliant with requirements of the + * {@code CONSTANT_Class_info} structure * @throws NullPointerException if the argument is {@code null} * @throws IllegalArgumentException if the name string is not in the * correct format - * @jvms 4.2.1 Binary Class and Interface Names * @see ClassDesc#of(String) * @see ClassDesc#ofDescriptor(String) + * @see ClassDesc#internalName() + * @see The {@code CONSTANT_Class_info} Structure * @since 20 */ static ClassDesc ofInternalName(String name) { - ConstantUtils.validateInternalClassName(requireNonNull(name)); + if (name.isEmpty()) // implicit null check + throw new IllegalArgumentException("Invalid class name: "); + // Arrays + if (name.charAt(0) == '[') { + return ofDescriptor(name); + } + // Classes or Interfaces + ConstantUtils.validateInternalClassName(name); return ClassDesc.ofDescriptor("L" + name + ";"); } @@ -153,7 +164,6 @@ static ClassDesc of(String packageName, String className) { * @throws IllegalArgumentException if the name string is not in the * correct format * @jvms 4.3.2 Field Descriptors - * @jvms 4.4.1 The CONSTANT_Class_info Structure * @see ClassDesc#of(String) * @see ClassDesc#ofInternalName(String) */ @@ -181,7 +191,6 @@ static ClassDesc ofDescriptor(String descriptor) { * @return a {@linkplain ClassDesc} describing the array type * @throws IllegalStateException if the resulting {@linkplain * ClassDesc} would have an array rank of greater than 255 - * @jvms 4.4.1 The CONSTANT_Class_info Structure */ default ClassDesc arrayType() { int depth = ConstantUtils.arrayDepth(descriptorString()); @@ -202,7 +211,6 @@ default ClassDesc arrayType() { * @throws IllegalArgumentException if the rank is less than or * equal to zero or if the rank of the resulting array type is * greater than 255 - * @jvms 4.4.1 The CONSTANT_Class_info Structure */ default ClassDesc arrayType(int rank) { int netRank; @@ -357,18 +365,17 @@ else if (isArray()) { /** * {@return the string representation of this {@linkplain ClassDesc} in a - * {@code CONSTANT_Class_info} ({@jvms 4.4.1})} This is the binary name - * in internal form for classes and interfaces, and descriptors for arrays. - * Primitive types cannot be represented by {@code CONSTANT_Class_info} - * structures. + * {@link ##constant-class-info CONSTANT_Class_info} structure} * * @apiNote - * Unlike {@link #ofInternalName ofInternalName} that rejects array descriptors, - * this method returns the array descriptor string if this {@linkplain ClassDesc} - * represents an array. + * In a future release, this API may return a value instead of throwing + * an exception if this {@linkplain ClassDesc} becomes representable in a + * {@code CONSTANT_Class_info}. * * @throws IllegalStateException if this {@linkplain ClassDesc} describes a type * that cannot be represented by a {@code CONSTANT_Class_info}, such as primitive types + * @see ClassDesc#ofInternalName(String) + * @see The {@code CONSTANT_Class_info} Structure * @since 21 */ String internalName(); diff --git a/test/jdk/java/lang/constant/ClassDescTest.java b/test/jdk/java/lang/constant/ClassDescTest.java index ee561f33b37f2..71bf0e803fa4c 100644 --- a/test/jdk/java/lang/constant/ClassDescTest.java +++ b/test/jdk/java/lang/constant/ClassDescTest.java @@ -50,6 +50,9 @@ private void testClassDesc(ClassDesc r) throws ReflectiveOperationException { // Test descriptor accessor, factory, equals assertEquals(r, ClassDesc.ofDescriptor(r.descriptorString())); + if (!r.isPrimitive()) { + assertEquals(r, ClassDesc.ofInternalName(r.internalName())); + } if (!r.descriptorString().equals("V")) { assertEquals(r, r.arrayType().componentType()); @@ -79,7 +82,8 @@ private void testClassDesc(ClassDesc r, Class c) throws ReflectiveOperationEx assertEquals(ClassDesc.ofDescriptor(c.descriptorString()), r); if (!c.isPrimitive()) { - assertEquals(c.getName(), r.internalName().replace('/', '.')); + assertEquals(c.getName(), r.internalName().replace('/', '.')); // may be invalid in valhalla + assertEquals(r, ClassDesc.ofInternalName(c.getName().replace('.', '/'))); } } @@ -271,7 +275,7 @@ public void testBadClassDescs() { } } - List badInternalNames = List.of("I;", "[]", "[Ljava/lang/String;", + List badInternalNames = List.of("I;", "[]", "[Ljava.lang.String;", "Ljava.lang.String;", "java.lang.String"); for (String d : badInternalNames) { try {