Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 41 additions & 16 deletions src/java.base/share/classes/java/lang/constant/ClassDesc.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@
* {@linkplain ClassDesc} for the component type and then call the {@link #arrayType()}
* or {@link #arrayType(int)} methods.
*
* <h2 id="constant-class-info">The {@code CONSTANT_Class_info} Structure</h2>
*
* <p>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
Expand Down Expand Up @@ -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 <a href="#constant-class-info">The {@code CONSTANT_Class_info} Structure</a>
* @since 20

Choose a reason for hiding this comment

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

This should have @revised, as array descriptors are not allowed as input to this method in JDK 20.

Suggested change
* @since 20
* @since 20
* @revised 21

Copy link
Member Author

Choose a reason for hiding this comment

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

There's no guideline on using revised. Also, I don't think we will declare it revised if it starts accepting Valhalla Q-types.

*/
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 + ";");
}

Expand Down Expand Up @@ -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)
*/
Expand Down Expand Up @@ -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());
Expand All @@ -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;
Expand Down Expand Up @@ -355,6 +363,23 @@ else if (isArray()) {
throw new IllegalStateException(descriptorString());
}

/**
* {@return the string representation of this {@linkplain ClassDesc} in a
* {@link ##constant-class-info CONSTANT_Class_info} structure}
*
* @apiNote
* 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 <a href="#constant-class-info">The {@code CONSTANT_Class_info} Structure</a>
* @since 21
*/
String internalName();

/**
* Returns a field type descriptor string for this type
*
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Copy link
Member

Choose a reason for hiding this comment

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

I suggest to cache the internal name to avoid repeated substring-ing with each call.
ClassDesc instances are frequently used as static constants serving for generations or transformations of many classes, methods, instructions.
It is highly expected that if this method is invoked at least once on the particular instance, it will be invoked many times on the same instance.

}

@Override
public String descriptorString() {
return descriptor;
Expand Down
24 changes: 17 additions & 7 deletions test/jdk/java/lang/constant/ClassDescTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -55,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());
Expand All @@ -64,10 +62,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());
}
}

Expand All @@ -77,6 +80,11 @@ 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('/', '.')); // may be invalid in valhalla
assertEquals(r, ClassDesc.ofInternalName(c.getName().replace('.', '/')));
}
}

public void testSymbolicDescsConstants() throws ReflectiveOperationException {
Expand Down Expand Up @@ -118,6 +126,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()) {
Expand Down Expand Up @@ -265,7 +275,7 @@ public void testBadClassDescs() {
}
}

List<String> badInternalNames = List.of("I;", "[]", "[Ljava/lang/String;",
List<String> badInternalNames = List.of("I;", "[]", "[Ljava.lang.String;",
"Ljava.lang.String;", "java.lang.String");
for (String d : badInternalNames) {
try {
Expand Down