1 /* 2 * Copyright (c) 2015, 2023, 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 * @enablePreview 28 */ 29 30 import java.lang.classfile.ClassBuilder; 31 import java.lang.classfile.ClassFile; 32 import java.lang.classfile.CodeBuilder; 33 import java.lang.classfile.attribute.EnclosingMethodAttribute; 34 import java.lang.classfile.attribute.InnerClassInfo; 35 import java.lang.classfile.attribute.InnerClassesAttribute; 36 import java.lang.constant.ClassDesc; 37 import java.lang.reflect.AccessFlag; 38 import java.util.Optional; 39 40 import static java.lang.classfile.ClassFile.ACC_PUBLIC; 41 import static java.lang.classfile.ClassFile.ACC_STATIC; 42 import static java.lang.constant.ConstantDescs.CD_Object; 43 import static java.lang.constant.ConstantDescs.INIT_NAME; 44 import static java.lang.constant.ConstantDescs.MTD_void; 45 46 public class GetSimpleNameTest { 47 static class NestedClass {} 48 class InnerClass {} 49 50 static Class<?> f1() { 51 class LocalClass {} 52 return LocalClass.class; 53 } 54 55 public static void main(String[] args) throws Exception { 56 assertEquals(NestedClass.class.getSimpleName(), "NestedClass"); 57 assertEquals( InnerClass.class.getSimpleName(), "InnerClass"); 58 assertEquals( f1().getSimpleName(), "LocalClass"); 59 60 java.io.Serializable anon = new java.io.Serializable() {}; 61 assertEquals(anon.getClass().getSimpleName(), ""); 62 63 // Java class names, prepended enclosing class name. 64 testNested("p.Outer$Nested", "p.Outer", "Nested"); 65 testInner( "p.Outer$Inner", "p.Inner", "Inner"); 66 testLocal( "p.Outer$1Local", "p.Outer", "Local"); 67 testAnon( "p.Outer$1", "p.Outer", ""); 68 69 // Non-Java class names, prepended enclosing class name. 70 testNested("p.$C1$Nested", "p.$C1$", "Nested"); 71 testInner( "p.$C1$Inner", "p.$C1$", "Inner"); 72 testLocal( "p.$C1$Local", "p.$C1$", "Local"); 73 testAnon( "p.$C1$1", "p.$C1$", ""); 74 75 // Non-Java class names, unrelated class names. 76 testNested("p1.$Nested$", "p2.$C1$", "Nested"); 77 testInner( "p1.$Inner$", "p2.$C1$", "Inner"); 78 testLocal( "p1.$Local$", "p2.$C1$", "Local"); 79 testAnon( "p1.$anon$", "p2.$C1$", ""); 80 } 81 82 static void testNested(String innerName, String outerName, String simpleName) throws Exception { 83 BytecodeGenerator bg = new BytecodeGenerator(innerName, outerName, simpleName); 84 CustomCL cl = new CustomCL(innerName, outerName, bg.getNestedClasses(true), bg.getNestedClasses(false)); 85 assertEquals(cl.loadClass(innerName).getSimpleName(), simpleName); 86 } 87 88 static void testInner(String innerName, String outerName, String simpleName) throws Exception { 89 BytecodeGenerator bg = new BytecodeGenerator(innerName, outerName, simpleName); 90 CustomCL cl = new CustomCL(innerName, outerName, bg.getInnerClasses(true), bg.getInnerClasses(false)); 91 assertEquals(cl.loadClass(innerName).getSimpleName(), simpleName); 92 } 93 94 static void testLocal(String innerName, String outerName, String simpleName) throws Exception { 95 BytecodeGenerator bg = new BytecodeGenerator(innerName, outerName, simpleName); 96 CustomCL cl = new CustomCL(innerName, outerName, bg.getLocalClasses(true), bg.getLocalClasses(false)); 97 assertEquals(cl.loadClass(innerName).getSimpleName(), simpleName); 98 } 99 100 static void testAnon(String innerName, String outerName, String simpleName) throws Exception { 101 BytecodeGenerator bg = new BytecodeGenerator(innerName, outerName, simpleName); 102 CustomCL cl = new CustomCL(innerName, outerName, bg.getAnonymousClasses(true), bg.getAnonymousClasses(false)); 103 assertEquals(cl.loadClass(innerName).getSimpleName(), simpleName); 104 } 105 106 static void assertEquals(Object o1, Object o2) { 107 if (!java.util.Objects.equals(o1, o2)) { 108 throw new AssertionError(o1 + " != " + o2); 109 } 110 } 111 112 static class CustomCL extends ClassLoader { 113 final String innerName; 114 final String outerName; 115 116 final byte[] innerClassFile; 117 final byte[] outerClassFile; 118 119 CustomCL(String innerName, String outerName, byte[] innerClassFile, byte[] outerClassFile) { 120 this.innerName = innerName; 121 this.outerName = outerName; 122 this.innerClassFile = innerClassFile; 123 this.outerClassFile = outerClassFile; 124 } 125 @Override 126 protected Class<?> findClass(String name) throws ClassNotFoundException { 127 if (innerName.equals(name)) { 128 return defineClass(innerName, innerClassFile, 0, innerClassFile.length); 129 } else if (outerName.equals(name)) { 130 return defineClass(outerName, outerClassFile, 0, outerClassFile.length); 131 } else { 132 throw new ClassNotFoundException(name); 133 } 134 } 135 } 136 137 static class BytecodeGenerator { 138 final ClassDesc innerName; 139 final ClassDesc outerName; 140 final String simpleName; 141 142 BytecodeGenerator(String innerName, String outerName, String simpleName) { 143 this.innerName = ClassDesc.of(innerName); 144 this.outerName = ClassDesc.of(outerName); 145 this.simpleName = simpleName; 146 } 147 148 static void makeDefaultCtor(ClassBuilder clb) { 149 clb.withMethodBody(INIT_NAME, MTD_void, ACC_PUBLIC, cb -> { 150 cb.aload(0); 151 cb.invokespecial(CD_Object, INIT_NAME, MTD_void); 152 cb.return_(); 153 }); 154 } 155 156 void makeCtxk(ClassBuilder clb, boolean isInner) { 157 if (isInner) { 158 clb.with(EnclosingMethodAttribute.of(outerName, 159 Optional.of("f"), Optional.of(MTD_void))); 160 } else { 161 clb.withMethodBody("f", MTD_void, ACC_PUBLIC | ACC_STATIC, 162 CodeBuilder::return_); 163 } 164 } 165 166 byte[] getNestedClasses(boolean isInner) { 167 var name = (isInner ? innerName : outerName); 168 return ClassFile.of().build(name, clb -> { 169 clb.withSuperclass(CD_Object); 170 clb.withFlags(AccessFlag.PUBLIC, AccessFlag.SUPER); 171 clb.with(InnerClassesAttribute.of( 172 InnerClassInfo.of(innerName, 173 Optional.of(outerName), 174 Optional.of(simpleName)))); 175 makeDefaultCtor(clb); 176 }); 177 } 178 179 byte[] getInnerClasses(boolean isInner) { 180 var name = (isInner ? innerName : outerName); 181 return ClassFile.of().build(name, clb -> { 182 clb.withSuperclass(CD_Object); 183 clb.withFlags(AccessFlag.PUBLIC, AccessFlag.SUPER); 184 clb.with(InnerClassesAttribute.of( 185 InnerClassInfo.of(innerName, 186 Optional.of(outerName), 187 Optional.of(simpleName), 188 AccessFlag.PUBLIC))); 189 makeDefaultCtor(clb); 190 }); 191 } 192 193 byte[] getLocalClasses(boolean isInner) { 194 var name = (isInner ? innerName : outerName); 195 return ClassFile.of().build(name, clb -> { 196 clb.withSuperclass(CD_Object); 197 clb.withFlags(AccessFlag.PUBLIC, AccessFlag.SUPER); 198 clb.with(InnerClassesAttribute.of( 199 InnerClassInfo.of(innerName, 200 Optional.empty(), 201 Optional.of(simpleName), 202 AccessFlag.PUBLIC, AccessFlag.STATIC))); 203 makeDefaultCtor(clb); 204 makeCtxk(clb, isInner); 205 }); 206 } 207 208 byte[] getAnonymousClasses(boolean isInner) { 209 var name = (isInner ? innerName : outerName); 210 return ClassFile.of().build(name, clb -> { 211 clb.withSuperclass(CD_Object); 212 clb.withFlags(AccessFlag.PUBLIC, AccessFlag.SUPER); 213 clb.with(InnerClassesAttribute.of( 214 InnerClassInfo.of(innerName, 215 Optional.empty(), 216 Optional.empty(), 217 AccessFlag.PUBLIC, AccessFlag.STATIC))); 218 makeDefaultCtor(clb); 219 makeCtxk(clb, isInner); 220 }); 221 } 222 } 223 }