1 /* 2 * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* @test 25 * @bug 8057919 26 * @summary Class.getSimpleName() should work for non-JLS compliant class names 27 */ 28 29 import java.lang.classfile.ClassBuilder; 30 import java.lang.classfile.ClassFile; 31 import java.lang.classfile.CodeBuilder; 32 import java.lang.classfile.attribute.EnclosingMethodAttribute; 33 import java.lang.classfile.attribute.InnerClassInfo; 34 import java.lang.classfile.attribute.InnerClassesAttribute; 35 import java.lang.constant.ClassDesc; 36 import java.lang.reflect.AccessFlag; 37 import java.util.Optional; 38 39 import static java.lang.classfile.ClassFile.ACC_PUBLIC; 40 import static java.lang.classfile.ClassFile.ACC_STATIC; 41 import static java.lang.constant.ConstantDescs.CD_Object; 42 import static java.lang.constant.ConstantDescs.INIT_NAME; 43 import static java.lang.constant.ConstantDescs.MTD_void; 44 45 public class GetSimpleNameTest { 46 static class NestedClass {} 47 class InnerClass {} 48 49 static Class<?> f1() { 50 class LocalClass {} 51 return LocalClass.class; 52 } 53 54 public static void main(String[] args) throws Exception { 55 assertEquals(NestedClass.class.getSimpleName(), "NestedClass"); 56 assertEquals( InnerClass.class.getSimpleName(), "InnerClass"); 57 assertEquals( f1().getSimpleName(), "LocalClass"); 58 59 java.io.Serializable anon = new java.io.Serializable() {}; 60 assertEquals(anon.getClass().getSimpleName(), ""); 61 62 // Java class names, prepended enclosing class name. 63 testNested("p.Outer$Nested", "p.Outer", "Nested"); 64 testInner( "p.Outer$Inner", "p.Inner", "Inner"); 65 testLocal( "p.Outer$1Local", "p.Outer", "Local"); 66 testAnon( "p.Outer$1", "p.Outer", ""); 67 68 // Non-Java class names, prepended enclosing class name. 69 testNested("p.$C1$Nested", "p.$C1$", "Nested"); 70 testInner( "p.$C1$Inner", "p.$C1$", "Inner"); 71 testLocal( "p.$C1$Local", "p.$C1$", "Local"); 72 testAnon( "p.$C1$1", "p.$C1$", ""); 73 74 // Non-Java class names, unrelated class names. 75 testNested("p1.$Nested$", "p2.$C1$", "Nested"); 76 testInner( "p1.$Inner$", "p2.$C1$", "Inner"); 77 testLocal( "p1.$Local$", "p2.$C1$", "Local"); 78 testAnon( "p1.$anon$", "p2.$C1$", ""); 79 } 80 81 static void testNested(String innerName, String outerName, String simpleName) throws Exception { 82 BytecodeGenerator bg = new BytecodeGenerator(innerName, outerName, simpleName); 83 CustomCL cl = new CustomCL(innerName, outerName, bg.getNestedClasses(true), bg.getNestedClasses(false)); 84 assertEquals(cl.loadClass(innerName).getSimpleName(), simpleName); 85 } 86 87 static void testInner(String innerName, String outerName, String simpleName) throws Exception { 88 BytecodeGenerator bg = new BytecodeGenerator(innerName, outerName, simpleName); 89 CustomCL cl = new CustomCL(innerName, outerName, bg.getInnerClasses(true), bg.getInnerClasses(false)); 90 assertEquals(cl.loadClass(innerName).getSimpleName(), simpleName); 91 } 92 93 static void testLocal(String innerName, String outerName, String simpleName) throws Exception { 94 BytecodeGenerator bg = new BytecodeGenerator(innerName, outerName, simpleName); 95 CustomCL cl = new CustomCL(innerName, outerName, bg.getLocalClasses(true), bg.getLocalClasses(false)); 96 assertEquals(cl.loadClass(innerName).getSimpleName(), simpleName); 97 } 98 99 static void testAnon(String innerName, String outerName, String simpleName) throws Exception { 100 BytecodeGenerator bg = new BytecodeGenerator(innerName, outerName, simpleName); 101 CustomCL cl = new CustomCL(innerName, outerName, bg.getAnonymousClasses(true), bg.getAnonymousClasses(false)); 102 assertEquals(cl.loadClass(innerName).getSimpleName(), simpleName); 103 } 104 105 static void assertEquals(Object o1, Object o2) { 106 if (!java.util.Objects.equals(o1, o2)) { 107 throw new AssertionError(o1 + " != " + o2); 108 } 109 } 110 111 static class CustomCL extends ClassLoader { 112 final String innerName; 113 final String outerName; 114 115 final byte[] innerClassFile; 116 final byte[] outerClassFile; 117 118 CustomCL(String innerName, String outerName, byte[] innerClassFile, byte[] outerClassFile) { 119 this.innerName = innerName; 120 this.outerName = outerName; 121 this.innerClassFile = innerClassFile; 122 this.outerClassFile = outerClassFile; 123 } 124 @Override 125 protected Class<?> findClass(String name) throws ClassNotFoundException { 126 if (innerName.equals(name)) { 127 return defineClass(innerName, innerClassFile, 0, innerClassFile.length); 128 } else if (outerName.equals(name)) { 129 return defineClass(outerName, outerClassFile, 0, outerClassFile.length); 130 } else { 131 throw new ClassNotFoundException(name); 132 } 133 } 134 } 135 136 static class BytecodeGenerator { 137 final ClassDesc innerName; 138 final ClassDesc outerName; 139 final String simpleName; 140 141 BytecodeGenerator(String innerName, String outerName, String simpleName) { 142 this.innerName = ClassDesc.of(innerName); 143 this.outerName = ClassDesc.of(outerName); 144 this.simpleName = simpleName; 145 } 146 147 static void makeDefaultCtor(ClassBuilder clb) { 148 clb.withMethodBody(INIT_NAME, MTD_void, ACC_PUBLIC, cb -> { 149 cb.aload(0); 150 cb.invokespecial(CD_Object, INIT_NAME, MTD_void); 151 cb.return_(); 152 }); 153 } 154 155 void makeCtxk(ClassBuilder clb, boolean isInner) { 156 if (isInner) { 157 clb.with(EnclosingMethodAttribute.of(outerName, 158 Optional.of("f"), Optional.of(MTD_void))); 159 } else { 160 clb.withMethodBody("f", MTD_void, ACC_PUBLIC | ACC_STATIC, 161 CodeBuilder::return_); 162 } 163 } 164 165 byte[] getNestedClasses(boolean isInner) { 166 var name = (isInner ? innerName : outerName); 167 return ClassFile.of().build(name, clb -> { 168 clb.withSuperclass(CD_Object); 169 clb.withFlags(AccessFlag.PUBLIC, AccessFlag.SUPER); 170 clb.with(InnerClassesAttribute.of( 171 InnerClassInfo.of(innerName, 172 Optional.of(outerName), 173 Optional.of(simpleName)))); 174 makeDefaultCtor(clb); 175 }); 176 } 177 178 byte[] getInnerClasses(boolean isInner) { 179 var name = (isInner ? innerName : outerName); 180 return ClassFile.of().build(name, clb -> { 181 clb.withSuperclass(CD_Object); 182 clb.withFlags(AccessFlag.PUBLIC, AccessFlag.SUPER); 183 clb.with(InnerClassesAttribute.of( 184 InnerClassInfo.of(innerName, 185 Optional.of(outerName), 186 Optional.of(simpleName), 187 AccessFlag.PUBLIC))); 188 makeDefaultCtor(clb); 189 }); 190 } 191 192 byte[] getLocalClasses(boolean isInner) { 193 var name = (isInner ? innerName : outerName); 194 return ClassFile.of().build(name, clb -> { 195 clb.withSuperclass(CD_Object); 196 clb.withFlags(AccessFlag.PUBLIC, AccessFlag.SUPER); 197 clb.with(InnerClassesAttribute.of( 198 InnerClassInfo.of(innerName, 199 Optional.empty(), 200 Optional.of(simpleName), 201 AccessFlag.PUBLIC, AccessFlag.STATIC))); 202 makeDefaultCtor(clb); 203 makeCtxk(clb, isInner); 204 }); 205 } 206 207 byte[] getAnonymousClasses(boolean isInner) { 208 var name = (isInner ? innerName : outerName); 209 return ClassFile.of().build(name, clb -> { 210 clb.withSuperclass(CD_Object); 211 clb.withFlags(AccessFlag.PUBLIC, AccessFlag.SUPER); 212 clb.with(InnerClassesAttribute.of( 213 InnerClassInfo.of(innerName, 214 Optional.empty(), 215 Optional.empty(), 216 AccessFlag.PUBLIC, AccessFlag.STATIC))); 217 makeDefaultCtor(clb); 218 makeCtxk(clb, isInner); 219 }); 220 } 221 } 222 }