1f7d524fd3
Reviewed-by: mchung
401 lines
16 KiB
Java
401 lines
16 KiB
Java
/*
|
|
* Copyright (c) 2018, 2024, 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
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
import java.lang.invoke.MethodHandles;
|
|
import java.lang.constant.ClassDesc;
|
|
import java.lang.constant.ConstantDescs;
|
|
import java.lang.reflect.Array;
|
|
import java.lang.reflect.Field;
|
|
import java.lang.reflect.Modifier;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
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;
|
|
|
|
/*
|
|
* @test
|
|
* @bug 8215510 8283075 8338544
|
|
* @compile ClassDescTest.java
|
|
* @run testng ClassDescTest
|
|
* @summary unit tests for java.lang.constant.ClassDesc
|
|
*/
|
|
@Test
|
|
public class ClassDescTest extends SymbolicDescTest {
|
|
|
|
private void testClassDesc(ClassDesc r) throws ReflectiveOperationException {
|
|
testSymbolicDesc(r);
|
|
|
|
// Test descriptor accessor, factory, equals
|
|
assertEquals(r, ClassDesc.ofDescriptor(r.descriptorString()));
|
|
|
|
if (!r.descriptorString().equals("V")) {
|
|
assertEquals(r, r.arrayType().componentType());
|
|
// Commutativity: array -> resolve -> componentType -> toSymbolic
|
|
assertEquals(r, r.arrayType().resolveConstantDesc(LOOKUP).getComponentType().describeConstable().orElseThrow());
|
|
// Commutativity: resolve -> array -> toSymbolic -> component type
|
|
assertEquals(r, Array.newInstance(r.resolveConstantDesc(LOOKUP), 0).getClass().describeConstable().orElseThrow().componentType());
|
|
}
|
|
|
|
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());
|
|
} else {
|
|
assertNull(r.componentType());
|
|
}
|
|
|
|
if (!r.isClassOrInterface()) {
|
|
assertEquals(r.packageName(), "");
|
|
}
|
|
}
|
|
|
|
private static String classDisplayName(Class<?> c) {
|
|
int arrayLevel = 0;
|
|
while (c.isArray()) {
|
|
arrayLevel++;
|
|
c = c.componentType();
|
|
}
|
|
String name = c.getName();
|
|
String simpleClassName;
|
|
if (c.isPrimitive()) {
|
|
simpleClassName = name;
|
|
} else {
|
|
int lastDot = name.lastIndexOf('.');
|
|
simpleClassName = lastDot == -1 ? name : name.substring(lastDot + 1);
|
|
}
|
|
return simpleClassName + "[]".repeat(arrayLevel);
|
|
}
|
|
|
|
private void testClassDesc(ClassDesc r, Class<?> c) throws ReflectiveOperationException {
|
|
testClassDesc(r);
|
|
|
|
assertEquals(r.resolveConstantDesc(LOOKUP), c);
|
|
assertEquals(c.describeConstable().orElseThrow(), r);
|
|
assertEquals(ClassDesc.ofDescriptor(c.descriptorString()), r);
|
|
if (r.isArray()) {
|
|
testClassDesc(r.componentType(), c.componentType());
|
|
}
|
|
if (r.isClassOrInterface()) {
|
|
assertEquals(r.packageName(), c.getPackageName());
|
|
}
|
|
assertEquals(r.displayName(), classDisplayName(c));
|
|
}
|
|
|
|
public void testSymbolicDescsConstants() throws ReflectiveOperationException {
|
|
int tested = 0;
|
|
Field[] fields = ConstantDescs.class.getDeclaredFields();
|
|
for (Field f : fields) {
|
|
try {
|
|
if (f.getType().equals(ClassDesc.class)
|
|
&& ((f.getModifiers() & Modifier.STATIC) != 0)
|
|
&& ((f.getModifiers() & Modifier.PUBLIC) != 0)) {
|
|
ClassDesc cr = (ClassDesc) f.get(null);
|
|
Class<?> c = cr.resolveConstantDesc(MethodHandles.lookup());
|
|
testClassDesc(cr, c);
|
|
++tested;
|
|
}
|
|
}
|
|
catch (Throwable e) {
|
|
System.out.println(e.getMessage());
|
|
fail("Error testing field " + f.getName(), e);
|
|
}
|
|
}
|
|
|
|
assertTrue(tested > 0);
|
|
}
|
|
|
|
public void testPrimitiveClassDesc() throws ReflectiveOperationException {
|
|
for (Primitives p : Primitives.values()) {
|
|
List<ClassDesc> descs = List.of(ClassDesc.ofDescriptor(p.descriptor),
|
|
p.classDesc,
|
|
p.clazz.describeConstable().orElseThrow());
|
|
for (ClassDesc c : descs) {
|
|
testClassDesc(c, p.clazz);
|
|
assertTrue(c.isPrimitive());
|
|
assertEquals(p.descriptor, c.descriptorString());
|
|
assertEquals(p.name, c.displayName());
|
|
descs.forEach(cc -> assertEquals(c, cc));
|
|
if (p != Primitives.VOID) {
|
|
testClassDesc(c.arrayType(), p.arrayClass);
|
|
assertEquals(c, p.arrayClass.describeConstable().orElseThrow().componentType());
|
|
assertEquals(c, p.classDesc.arrayType().componentType());
|
|
}
|
|
}
|
|
|
|
for (Primitives other : Primitives.values()) {
|
|
ClassDesc otherDescr = ClassDesc.ofDescriptor(other.descriptor);
|
|
if (p != other)
|
|
descs.forEach(c -> assertNotEquals(c, otherDescr));
|
|
else
|
|
descs.forEach(c -> assertEquals(c, otherDescr));
|
|
}
|
|
}
|
|
}
|
|
|
|
public void testSimpleClassDesc() throws ReflectiveOperationException {
|
|
|
|
List<ClassDesc> stringClassDescs = Arrays.asList(ClassDesc.ofDescriptor("Ljava/lang/String;"),
|
|
ClassDesc.ofInternalName("java/lang/String"),
|
|
ClassDesc.of("java.lang", "String"),
|
|
ClassDesc.of("java.lang.String"),
|
|
ClassDesc.of("java.lang.String").arrayType().componentType(),
|
|
String.class.describeConstable().orElseThrow());
|
|
for (ClassDesc r : stringClassDescs) {
|
|
testClassDesc(r, String.class);
|
|
assertFalse(r.isPrimitive());
|
|
assertEquals("Ljava/lang/String;", r.descriptorString());
|
|
assertEquals("String", r.displayName());
|
|
testClassDesc(r.arrayType(), String[].class);
|
|
testClassDesc(r.arrayType(3), String[][][].class);
|
|
stringClassDescs.forEach(rr -> assertEquals(r, rr));
|
|
}
|
|
|
|
testClassDesc(ClassDesc.of("java.lang.String").arrayType(), String[].class);
|
|
testClassDesc(ClassDesc.of("java.util.Map").nested("Entry"), Map.Entry.class);
|
|
|
|
assertEquals(ClassDesc.of("java.lang.String"), ClassDesc.ofDescriptor("Ljava/lang/String;"));
|
|
assertEquals(ClassDesc.of("java.lang.String"), ClassDesc.ofInternalName("java/lang/String"));
|
|
|
|
ClassDesc thisClassDesc = ClassDesc.ofDescriptor("LClassDescTest;");
|
|
assertEquals(thisClassDesc, ClassDesc.of("", "ClassDescTest"));
|
|
assertEquals(thisClassDesc, ClassDesc.of("ClassDescTest"));
|
|
assertEquals(thisClassDesc.displayName(), "ClassDescTest");
|
|
testClassDesc(thisClassDesc, ClassDescTest.class);
|
|
}
|
|
|
|
public void testPackageName() {
|
|
assertEquals("com.foo", ClassDesc.of("com.foo.Bar").packageName());
|
|
assertEquals("com.foo", ClassDesc.of("com.foo.Bar").nested("Baz").packageName());
|
|
assertEquals("", ClassDesc.of("Bar").packageName());
|
|
assertEquals("", ClassDesc.of("Bar").nested("Baz").packageName());
|
|
assertEquals("", ClassDesc.of("Bar").nested("Baz", "Foo").packageName());
|
|
|
|
assertEquals("", ConstantDescs.CD_int.packageName());
|
|
assertEquals("", ConstantDescs.CD_int.arrayType().packageName());
|
|
assertEquals("", ConstantDescs.CD_String.arrayType().packageName());
|
|
assertEquals("", ClassDesc.of("Bar").arrayType().packageName());
|
|
}
|
|
|
|
private void testBadArrayRank(ClassDesc cr) {
|
|
try {
|
|
cr.arrayType(-1);
|
|
fail("");
|
|
} catch (IllegalArgumentException e) {
|
|
// good
|
|
}
|
|
try {
|
|
cr.arrayType(0);
|
|
fail("");
|
|
} catch (IllegalArgumentException e) {
|
|
// good
|
|
}
|
|
}
|
|
|
|
private void testArrayRankOverflow() {
|
|
ClassDesc TwoDArrayDesc =
|
|
String.class.describeConstable().get().arrayType().arrayType();
|
|
|
|
try {
|
|
TwoDArrayDesc.arrayType(Integer.MAX_VALUE);
|
|
fail("");
|
|
} catch (IllegalArgumentException iae) {
|
|
// Expected
|
|
}
|
|
}
|
|
|
|
|
|
public void testArrayClassDesc() throws ReflectiveOperationException {
|
|
for (String d : basicDescs) {
|
|
ClassDesc a0 = ClassDesc.ofDescriptor(d);
|
|
ClassDesc a1 = a0.arrayType();
|
|
ClassDesc a2 = a1.arrayType();
|
|
|
|
testClassDesc(a0);
|
|
testClassDesc(a1);
|
|
testClassDesc(a2);
|
|
assertFalse(a0.isArray());
|
|
assertTrue(a1.isArray());
|
|
assertTrue(a2.isArray());
|
|
assertFalse(a1.isPrimitive());
|
|
assertFalse(a2.isPrimitive());
|
|
assertEquals(a0.descriptorString(), d);
|
|
assertEquals(a1.descriptorString(), "[" + a0.descriptorString());
|
|
assertEquals(a2.descriptorString(), "[[" + a0.descriptorString());
|
|
|
|
assertNull(a0.componentType());
|
|
assertEquals(a0, a1.componentType());
|
|
assertEquals(a1, a2.componentType());
|
|
|
|
assertNotEquals(a0, a1);
|
|
assertNotEquals(a1, a2);
|
|
|
|
assertEquals(a1, ClassDesc.ofDescriptor("[" + d));
|
|
assertEquals(a2, ClassDesc.ofDescriptor("[[" + d));
|
|
assertEquals(classToDescriptor(a0.resolveConstantDesc(LOOKUP)), a0.descriptorString());
|
|
assertEquals(classToDescriptor(a1.resolveConstantDesc(LOOKUP)), a1.descriptorString());
|
|
assertEquals(classToDescriptor(a2.resolveConstantDesc(LOOKUP)), a2.descriptorString());
|
|
|
|
testBadArrayRank(ConstantDescs.CD_int);
|
|
testBadArrayRank(ConstantDescs.CD_String);
|
|
testBadArrayRank(ClassDesc.of("Bar"));
|
|
testArrayRankOverflow();
|
|
}
|
|
try {
|
|
ConstantDescs.CD_void.arrayType();
|
|
fail("Should throw IAE");
|
|
} catch (IllegalArgumentException iae) {
|
|
// Expected
|
|
}
|
|
}
|
|
|
|
public void testBadClassDescs() {
|
|
List<String> badDescriptors = List.of("II", "I;", "Q", "L", "",
|
|
"java.lang.String", "[]", "Ljava/lang/String",
|
|
"Ljava.lang.String;", "java/lang/String", "L;",
|
|
"La//b;", "L/a;", "La/;");
|
|
|
|
for (String d : badDescriptors) {
|
|
try {
|
|
ClassDesc constant = ClassDesc.ofDescriptor(d);
|
|
fail(d);
|
|
}
|
|
catch (IllegalArgumentException e) {
|
|
// good
|
|
}
|
|
}
|
|
|
|
List<String> badBinaryNames = List.of("I;", "[]", "Ljava/lang/String",
|
|
"Ljava.lang.String;", "java/lang/String", "");
|
|
for (String d : badBinaryNames) {
|
|
try {
|
|
ClassDesc constant = ClassDesc.of(d);
|
|
fail(d);
|
|
} catch (IllegalArgumentException e) {
|
|
// good
|
|
}
|
|
}
|
|
|
|
List<String> badInternalNames = List.of("I;", "[]", "[Ljava/lang/String;",
|
|
"Ljava.lang.String;", "java.lang.String", "");
|
|
for (String d : badInternalNames) {
|
|
try {
|
|
ClassDesc constant = ClassDesc.ofInternalName(d);
|
|
fail(d);
|
|
} catch (IllegalArgumentException e) {
|
|
// good
|
|
}
|
|
}
|
|
|
|
for (Primitives p : Primitives.values()) {
|
|
testBadNestedClasses(ClassDesc.ofDescriptor(p.descriptor), "any");
|
|
testBadNestedClasses(ClassDesc.ofDescriptor(p.descriptor), "any", "other");
|
|
}
|
|
|
|
ClassDesc stringDesc = ClassDesc.ofDescriptor("Ljava/lang/String;");
|
|
ClassDesc stringArrDesc = stringDesc.arrayType(255);
|
|
try {
|
|
ClassDesc arrGreaterThan255 = stringArrDesc.arrayType();
|
|
fail("can't create an array type descriptor with more than 255 dimensions");
|
|
} catch (IllegalStateException e) {
|
|
// good
|
|
}
|
|
String descWith255ArrayDims = new String(new char[255]).replace('\0', '[');
|
|
try {
|
|
ClassDesc arrGreaterThan255 = ClassDesc.ofDescriptor(descWith255ArrayDims + "[Ljava/lang/String;");
|
|
fail("can't create an array type descriptor with more than 255 dimensions");
|
|
} catch (IllegalArgumentException e) {
|
|
// good
|
|
}
|
|
try {
|
|
ClassDesc arrWith255Dims = ClassDesc.ofDescriptor(descWith255ArrayDims + "Ljava/lang/String;");
|
|
arrWith255Dims.arrayType(1);
|
|
fail("can't create an array type descriptor with more than 255 dimensions");
|
|
} catch (IllegalArgumentException e) {
|
|
// good
|
|
}
|
|
}
|
|
|
|
private void testBadNestedClasses(ClassDesc cr, String firstNestedName, String... moreNestedNames) {
|
|
try {
|
|
cr.nested(firstNestedName, moreNestedNames);
|
|
fail("");
|
|
} catch (IllegalStateException e) {
|
|
// good
|
|
}
|
|
}
|
|
|
|
public void testLangClasses() {
|
|
Double d = 1.0;
|
|
assertEquals(d.resolveConstantDesc(LOOKUP), d);
|
|
assertEquals(d.describeConstable().get(), d);
|
|
|
|
Integer i = 1;
|
|
assertEquals(i.resolveConstantDesc(LOOKUP), i);
|
|
assertEquals(i.describeConstable().get(), i);
|
|
|
|
Float f = 1.0f;
|
|
assertEquals(f.resolveConstantDesc(LOOKUP), f);
|
|
assertEquals(f.describeConstable().get(), f);
|
|
|
|
Long l = 1L;
|
|
assertEquals(l.resolveConstantDesc(LOOKUP), l);
|
|
assertEquals(l.describeConstable().get(), l);
|
|
|
|
String s = "";
|
|
assertEquals(s.resolveConstantDesc(LOOKUP), s);
|
|
assertEquals(s.describeConstable().get(), s);
|
|
}
|
|
|
|
public void testNullNestedClasses() {
|
|
ClassDesc cd = ClassDesc.of("Bar");
|
|
try {
|
|
cd.nested(null);
|
|
fail("");
|
|
} catch (NullPointerException e) {
|
|
// good
|
|
}
|
|
|
|
try {
|
|
cd.nested("good", null);
|
|
fail("");
|
|
} catch (NullPointerException e) {
|
|
// good
|
|
}
|
|
|
|
try {
|
|
cd.nested("good", "goodToo", null);
|
|
fail("");
|
|
} catch (NullPointerException e) {
|
|
// good
|
|
}
|
|
}
|
|
}
|