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 * @enablePreview
27 * @summary Class.getSimpleName() should work for non-JLS compliant class names
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.IDENTITY);
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.IDENTITY);
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.IDENTITY);
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.IDENTITY);
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 }