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 /*
25 * @test
26 * @library /test/lib
27 * @build jdk.test.lib.Utils
28 * jdk.test.lib.compiler.CompilerUtils
29 * BasicTest
30 * @run testng/othervm BasicTest
31 * @run testng/othervm -Xcomp BasicTest
32 */
33
34 import java.io.File;
35 import java.io.IOException;
36 import java.lang.invoke.MethodHandles.Lookup;
37
38 import static java.lang.invoke.MethodHandles.lookup;
39 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
40
41 import java.lang.reflect.Array;
42 import java.lang.reflect.Method;
43 import java.nio.charset.StandardCharsets;
44 import java.nio.file.Files;
45 import java.nio.file.Path;
46 import java.nio.file.Paths;
47 import java.util.Arrays;
48 import java.util.List;
49 import java.util.stream.Stream;
50
51 import jdk.test.lib.compiler.CompilerUtils;
52 import jdk.test.lib.Utils;
53
54 import org.testng.annotations.BeforeTest;
55 import org.testng.annotations.DataProvider;
56 import org.testng.annotations.Test;
57 import static org.testng.Assert.*;
58
59 interface HiddenTest {
60 void test();
61 }
62
63 public class BasicTest {
64
65 private static final Path SRC_DIR = Paths.get(Utils.TEST_SRC, "src");
66 private static final Path CLASSES_DIR = Paths.get("classes");
67 private static final Path CLASSES_10_DIR = Paths.get("classes_10");
68
69 private static byte[] hiddenClassBytes;
70
71 @BeforeTest
72 static void setup() throws IOException {
73 compileSources(SRC_DIR, CLASSES_DIR);
74
75 hiddenClassBytes = Files.readAllBytes(CLASSES_DIR.resolve("HiddenClass.class"));
76
77 // compile with --release 10 with no NestHost and NestMembers attribute
78 compileSources(SRC_DIR.resolve("Outer.java"), CLASSES_10_DIR, "--release", "10");
79 compileSources(SRC_DIR.resolve("EnclosingClass.java"), CLASSES_10_DIR, "--release", "10");
80 }
81
82 static void compileSources(Path sourceFile, Path dest, String... options) throws IOException {
83 Stream<String> ops = Stream.of("-cp", Utils.TEST_CLASSES + File.pathSeparator + CLASSES_DIR);
84 if (options != null && options.length > 0) {
85 ops = Stream.concat(ops, Arrays.stream(options));
86 }
87 if (!CompilerUtils.compile(sourceFile, dest, ops.toArray(String[]::new))) {
88 throw new RuntimeException("Compilation of the test failed: " + sourceFile);
89 }
90 }
91
92 static Class<?> defineHiddenClass(String name) throws Exception {
93 byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve(name + ".class"));
94 Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
95 assertHiddenClass(hc);
96 singletonNest(hc);
97 return hc;
98 }
99
100 // basic test on a hidden class
101 @Test
102 public void hiddenClass() throws Throwable {
103 HiddenTest t = (HiddenTest)defineHiddenClass("HiddenClass").newInstance();
104 t.test();
105
106 // sanity check
107 Class<?> c = t.getClass();
108 Class<?>[] intfs = c.getInterfaces();
109 assertTrue(c.isHiddenClass());
110 assertFalse(c.isPrimitive());
111 assertTrue(intfs.length == 1);
112 assertTrue(intfs[0] == HiddenTest.class);
113 assertTrue(c.getCanonicalName() == null);
114 assertTrue(c.getName().startsWith("HiddenClass/"));
115
116 // test array of hidden class
117 testHiddenArray(c);
118
119 // test setAccessible
120 checkSetAccessible(c, "realTest");
121 checkSetAccessible(c, "test");
122 }
123
124 @Test
125 public void primitiveClass() {
126 assertFalse(int.class.isHiddenClass());
127 assertFalse(String.class.isHiddenClass());
128 }
129
130 private void testHiddenArray(Class<?> type) throws Exception {
131 // array of hidden class
132 Object array = Array.newInstance(type, 2);
133 Class<?> arrayType = array.getClass();
134 assertTrue(arrayType.isArray());
135 assertTrue(Array.getLength(array) == 2);
136 assertFalse(arrayType.isHiddenClass());
137 assertTrue(arrayType.getName().startsWith("[LHiddenClass/"), "unexpected name: " + arrayType.getName());
138
139 assertTrue(arrayType.getComponentType().isHiddenClass());
140 assertTrue(arrayType.getComponentType() == type);
141 Object t = type.newInstance();
142 Array.set(array, 0, t);
143 Object o = Array.get(array, 0);
144 assertTrue(o == t);
145 }
146
147 private void checkSetAccessible(Class<?> c, String name, Class<?>... ptypes) throws Exception {
148 Method m = c.getDeclaredMethod(name, ptypes);
149 assertTrue(m.trySetAccessible());
150 m.setAccessible(true);
151 }
152
153 // Define a hidden class that uses lambda
154 // This verifies LambdaMetaFactory supports the caller which is a hidden class
155 @Test
156 public void testLambda() throws Throwable {
157 HiddenTest t = (HiddenTest)defineHiddenClass("Lambda").newInstance();
158 try {
159 t.test();
171 Lookup lookup1 = lookup().defineHiddenClass(hc1, false);
172 Class<?> host = lookup1.lookupClass();
173
174 byte[] hc2 = Files.readAllBytes(CLASSES_DIR.resolve("Lambda.class"));
175 Lookup lookup2 = lookup1.defineHiddenClass(hc2, false, NESTMATE);
176 Class<?> member = lookup2.lookupClass();
177
178 // test nest membership and reflection API
179 assertTrue(host.isNestmateOf(member));
180 assertTrue(host.getNestHost() == host);
181 // getNestHost and getNestMembers return the same value when calling
182 // on a nest member and the nest host
183 assertTrue(member.getNestHost() == host.getNestHost());
184 assertTrue(Arrays.equals(member.getNestMembers(), host.getNestMembers()));
185 // getNestMembers includes the nest host that can be a hidden class but
186 // only includes static nest members
187 assertTrue(host.getNestMembers().length == 1);
188 assertTrue(host.getNestMembers()[0] == host);
189 }
190
191 @Test
192 public void hiddenCantReflect() throws Throwable {
193 HiddenTest t = (HiddenTest)defineHiddenClass("HiddenCantReflect").newInstance();
194 t.test();
195
196 Class<?> c = t.getClass();
197 Class<?>[] intfs = c.getInterfaces();
198 assertTrue(intfs.length == 1);
199 assertTrue(intfs[0] == HiddenTest.class);
200
201 try {
202 // this would cause loading of class HiddenCantReflect and NCDFE due
203 // to error during verification
204 c.getDeclaredMethods();
205 } catch (NoClassDefFoundError e) {
206 Throwable x = e.getCause();
207 if (x == null || !(x instanceof ClassNotFoundException && x.getMessage().contains("HiddenCantReflect"))) {
208 throw e;
209 }
210 }
211 }
212
213 @DataProvider(name = "hiddenClasses")
214 private Object[][] hiddenClasses() {
215 return new Object[][] {
216 new Object[] { "HiddenInterface", false },
217 new Object[] { "AbstractClass", false },
218 // class file with bad NestHost, NestMembers and InnerClasses or EnclosingMethod attribute
219 // define them as nestmate to verify Class::getNestHost and getNestMembers
220 new Object[] { "Outer", true },
221 new Object[] { "Outer$Inner", true },
222 new Object[] { "EnclosingClass", true },
223 new Object[] { "EnclosingClass$1", true },
224 };
225 }
226
227 @Test(dataProvider = "hiddenClasses")
228 public void defineHiddenClass(String name, boolean nestmate) throws Exception {
229 byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve(name + ".class"));
230 Class<?> hc;
231 Class<?> host;
232 if (nestmate) {
233 hc = lookup().defineHiddenClass(bytes, false, NESTMATE).lookupClass();
234 host = lookup().lookupClass().getNestHost();
235 } else {
236 hc = lookup().defineHiddenClass(bytes, false).lookupClass();
237 host = hc;
238 }
239 assertTrue(hc.getNestHost() == host);
240 assertTrue(hc.getNestMembers().length == 1);
241 assertTrue(hc.getNestMembers()[0] == host);
242 }
243
244 @Test(expectedExceptions = NoClassDefFoundError.class)
245 public void hiddenSuperClass() throws Exception {
246 byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve("HiddenSuper.class"));
247 Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
248 }
249
250 @Test(expectedExceptions = {IllegalArgumentException.class})
251 public void cantDefineModule() throws Throwable {
252 Path src = Paths.get("module-info.java");
253 Path dir = CLASSES_DIR.resolve("m");
254 Files.write(src, List.of("module m {}"), StandardCharsets.UTF_8);
255 compileSources(src, dir);
256
257 byte[] bytes = Files.readAllBytes(dir.resolve("module-info.class"));
258 lookup().defineHiddenClass(bytes, false);
259 }
260
261 @Test(expectedExceptions = {IllegalArgumentException.class})
262 public void cantDefineClassInAnotherPackage() throws Throwable {
263 Path src = Paths.get("ClassInAnotherPackage.java");
264 Files.write(src, List.of("package p;", "public class ClassInAnotherPackage {}"), StandardCharsets.UTF_8);
265 compileSources(src, CLASSES_DIR);
266
267 byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve("p").resolve("ClassInAnotherPackage.class"));
295 // define a hidden class with static nest membership
296 @Test
297 public void hasStaticNestHost() throws Exception {
298 byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve("Outer$Inner.class"));
299 Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
300 hiddenClassWithBadAttribute(hc, "Outer");
301 }
302
303 @Test
304 public void hasStaticNestMembers() throws Throwable {
305 byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve("Outer.class"));
306 Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
307 assertHiddenClass(hc);
308 assertTrue(hc.getNestHost() == hc);
309 Class<?>[] members = hc.getNestMembers();
310 assertTrue(members.length == 1 && members[0] == hc);
311 }
312
313 // a hidden class with bad InnerClasses or EnclosingMethod attribute
314 private void hiddenClassWithBadAttribute(Class<?> hc, String badDeclaringClassName) {
315 assertTrue(hc.isHiddenClass());
316 assertTrue(hc.getCanonicalName() == null);
317 assertTrue(hc.getName().contains("/"));
318
319 if (badDeclaringClassName == null) {
320 // the following reflection API assumes a good name in InnerClasses
321 // or EnclosingMethod attribute can successfully be resolved.
322 assertTrue(hc.getSimpleName().length() > 0);
323 assertFalse(hc.isAnonymousClass());
324 assertFalse(hc.isLocalClass());
325 assertFalse(hc.isMemberClass());
326 } else {
327 declaringClassNotFound(hc, badDeclaringClassName);
328 }
329
330 // validation of nest membership
331 assertTrue(hc.getNestHost() == hc);
332 // validate the static nest membership
333 Class<?>[] members = hc.getNestMembers();
334 assertTrue(members.length == 1 && members[0] == hc);
335 }
346 }
347 }
348 try {
349 // fail to find declaring/enclosing class
350 c.getSimpleName();
351 assertTrue(false);
352 } catch (NoClassDefFoundError e) {
353 if (!e.getMessage().equals(cn)) {
354 throw e;
355 }
356 }
357 }
358
359 private static void singletonNest(Class<?> hc) {
360 assertTrue(hc.getNestHost() == hc);
361 assertTrue(hc.getNestMembers().length == 1);
362 assertTrue(hc.getNestMembers()[0] == hc);
363 }
364
365 private static void assertHiddenClass(Class<?> hc) {
366 assertTrue(hc.isHiddenClass());
367 assertTrue(hc.getCanonicalName() == null);
368 assertTrue(hc.getName().contains("/"));
369 assertFalse(hc.isAnonymousClass());
370 assertFalse(hc.isLocalClass());
371 assertFalse(hc.isMemberClass());
372 assertFalse(hc.getSimpleName().isEmpty()); // sanity check
373 }
374 }
|
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 /*
25 * @test
26 * @modules java.base/jdk.internal.org.objectweb.asm
27 * jdk.compiler
28 * @library /test/lib
29 * @build jdk.test.lib.Utils
30 * jdk.test.lib.compiler.CompilerUtils
31 * @run testng/othervm --enable-preview BasicTest
32 */
33
34 import java.io.File;
35 import java.io.IOException;
36 import java.lang.invoke.MethodHandles.Lookup;
37
38 import static java.lang.invoke.MethodHandles.lookup;
39 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
40
41 import java.lang.reflect.Array;
42 import java.lang.reflect.Method;
43 import java.nio.charset.StandardCharsets;
44 import java.nio.file.Files;
45 import java.nio.file.Path;
46 import java.nio.file.Paths;
47 import java.util.Arrays;
48 import java.util.List;
49 import java.util.stream.Stream;
50
51 import jdk.internal.org.objectweb.asm.ClassWriter;
52 import jdk.internal.org.objectweb.asm.Type;
53 import jdk.test.lib.compiler.CompilerUtils;
54 import jdk.test.lib.Utils;
55
56 import org.testng.annotations.BeforeTest;
57 import org.testng.annotations.DataProvider;
58 import org.testng.annotations.Test;
59
60 import static jdk.internal.org.objectweb.asm.Opcodes.*;
61 import static org.testng.Assert.*;
62
63 interface HiddenTest {
64 void test();
65 }
66
67 public class BasicTest {
68
69 private static final Path SRC_DIR = Paths.get(Utils.TEST_SRC, "src");
70 private static final Path CLASSES_DIR = Paths.get("classes");
71 private static final Path CLASSES_10_DIR = Paths.get("classes_10");
72
73 private static byte[] hiddenClassBytes;
74
75 @BeforeTest
76 static void setup() throws IOException {
77 compileSources(SRC_DIR, CLASSES_DIR,
78 "--enable-preview", "-source", String.valueOf(Runtime.version().feature()));
79 hiddenClassBytes = Files.readAllBytes(CLASSES_DIR.resolve("HiddenClass.class"));
80
81 // compile with --release 10 with no NestHost and NestMembers attribute
82 compileSources(SRC_DIR.resolve("Outer.java"), CLASSES_10_DIR, "--release", "10");
83 compileSources(SRC_DIR.resolve("EnclosingClass.java"), CLASSES_10_DIR, "--release", "10");
84 }
85
86 static void compileSources(Path sourceFile, Path dest, String... options) throws IOException {
87 Stream<String> ops = Stream.of("-cp", Utils.TEST_CLASSES + File.pathSeparator + CLASSES_DIR);
88 if (options != null && options.length > 0) {
89 ops = Stream.concat(ops, Arrays.stream(options));
90 }
91 if (!CompilerUtils.compile(sourceFile, dest, ops.toArray(String[]::new))) {
92 throw new RuntimeException("Compilation of the test failed: " + sourceFile);
93 }
94 }
95
96 static Class<?> defineHiddenClass(String name) throws Exception {
97 byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve(name + ".class"));
98 Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
99 assertHiddenClass(hc);
100 singletonNest(hc);
101 return hc;
102 }
103
104 // basic test on a hidden class
105 @Test
106 public void hiddenClass() throws Throwable {
107 HiddenTest t = (HiddenTest)defineHiddenClass("HiddenClass").newInstance();
108 t.test();
109
110 // sanity check
111 Class<?> c = t.getClass();
112 Class<?>[] intfs = c.getInterfaces();
113 assertTrue(c.isHidden());
114 assertFalse(c.isPrimitive());
115 assertTrue(intfs.length == 1);
116 assertTrue(intfs[0] == HiddenTest.class);
117 assertTrue(c.getCanonicalName() == null);
118 assertTrue(c.getName().startsWith("HiddenClass/"));
119
120 // test array of hidden class
121 testHiddenArray(c);
122
123 // test setAccessible
124 checkSetAccessible(c, "realTest");
125 checkSetAccessible(c, "test");
126 }
127
128 // primitive class is not a hidden class
129 @Test
130 public void primitiveClass() {
131 assertFalse(int.class.isHidden());
132 assertFalse(String.class.isHidden());
133 }
134
135 private void testHiddenArray(Class<?> type) throws Exception {
136 // array of hidden class
137 Object array = Array.newInstance(type, 2);
138 Class<?> arrayType = array.getClass();
139 assertTrue(arrayType.isArray());
140 assertTrue(Array.getLength(array) == 2);
141 assertFalse(arrayType.isHidden());
142 assertTrue(arrayType.getName().startsWith("[LHiddenClass/"), "unexpected name: " + arrayType.getName());
143
144 assertTrue(arrayType.getComponentType().isHidden());
145 assertTrue(arrayType.getComponentType() == type);
146 Object t = type.newInstance();
147 Array.set(array, 0, t);
148 Object o = Array.get(array, 0);
149 assertTrue(o == t);
150 }
151
152 private void checkSetAccessible(Class<?> c, String name, Class<?>... ptypes) throws Exception {
153 Method m = c.getDeclaredMethod(name, ptypes);
154 assertTrue(m.trySetAccessible());
155 m.setAccessible(true);
156 }
157
158 // Define a hidden class that uses lambda
159 // This verifies LambdaMetaFactory supports the caller which is a hidden class
160 @Test
161 public void testLambda() throws Throwable {
162 HiddenTest t = (HiddenTest)defineHiddenClass("Lambda").newInstance();
163 try {
164 t.test();
176 Lookup lookup1 = lookup().defineHiddenClass(hc1, false);
177 Class<?> host = lookup1.lookupClass();
178
179 byte[] hc2 = Files.readAllBytes(CLASSES_DIR.resolve("Lambda.class"));
180 Lookup lookup2 = lookup1.defineHiddenClass(hc2, false, NESTMATE);
181 Class<?> member = lookup2.lookupClass();
182
183 // test nest membership and reflection API
184 assertTrue(host.isNestmateOf(member));
185 assertTrue(host.getNestHost() == host);
186 // getNestHost and getNestMembers return the same value when calling
187 // on a nest member and the nest host
188 assertTrue(member.getNestHost() == host.getNestHost());
189 assertTrue(Arrays.equals(member.getNestMembers(), host.getNestMembers()));
190 // getNestMembers includes the nest host that can be a hidden class but
191 // only includes static nest members
192 assertTrue(host.getNestMembers().length == 1);
193 assertTrue(host.getNestMembers()[0] == host);
194 }
195
196 @DataProvider(name = "hiddenClasses")
197 private Object[][] hiddenClasses() {
198 return new Object[][] {
199 new Object[] { "HiddenInterface", false },
200 new Object[] { "AbstractClass", false },
201 // a hidden annotation is useless because it cannot be referenced by any class
202 new Object[] { "HiddenAnnotation", false },
203 // class file with bad NestHost, NestMembers and InnerClasses or EnclosingMethod attribute
204 // define them as nestmate to verify Class::getNestHost and getNestMembers
205 new Object[] { "Outer", true },
206 new Object[] { "Outer$Inner", true },
207 new Object[] { "EnclosingClass", true },
208 new Object[] { "EnclosingClass$1", true },
209 };
210 }
211
212 /*
213 * Test that class file bytes that can be defined as a normal class
214 * can be successfully created as a hidden class even it might not
215 * make sense as a hidden class. For example, a hidden annotation
216 * is not useful as it cannot be referenced and an outer/inner class
217 * when defined as a hidden effectively becomes a final top-level class.
218 */
219 @Test(dataProvider = "hiddenClasses")
220 public void defineHiddenClass(String name, boolean nestmate) throws Exception {
221 byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve(name + ".class"));
222 Class<?> hc;
223 Class<?> host;
224 if (nestmate) {
225 hc = lookup().defineHiddenClass(bytes, false, NESTMATE).lookupClass();
226 host = lookup().lookupClass().getNestHost();
227 } else {
228 hc = lookup().defineHiddenClass(bytes, false).lookupClass();
229 host = hc;
230 }
231 assertTrue(hc.getNestHost() == host);
232 assertTrue(hc.getNestMembers().length == 1);
233 assertTrue(hc.getNestMembers()[0] == host);
234 }
235
236 @DataProvider(name = "emptyClasses")
237 private Object[][] emptyClasses() {
238 return new Object[][] {
239 new Object[] { "EmptyHiddenSynthetic", ACC_SYNTHETIC },
240 new Object[] { "EmptyHiddenEnum", ACC_ENUM },
241 new Object[] { "EmptyHiddenAbstractClass", ACC_ABSTRACT },
242 new Object[] { "EmptyHiddenInterface", ACC_ABSTRACT|ACC_INTERFACE },
243 new Object[] { "EmptyHiddenAnnotation", ACC_ANNOTATION|ACC_ABSTRACT|ACC_INTERFACE },
244 };
245 }
246
247 /*
248 * Test if an empty class with valid access flags can be created as a hidden class
249 * as long as it does not violate the restriction of a hidden class.
250 *
251 * A meaningful enum type defines constants of that enum type. So
252 * enum class containing constants of its type should not be a hidden
253 * class.
254 */
255 @Test(dataProvider = "emptyClasses")
256 public void emptyHiddenClass(String name, int accessFlags) throws Exception {
257 byte[] bytes = (accessFlags == ACC_ENUM) ? classBytes(name, Enum.class, accessFlags)
258 : classBytes(name, accessFlags);
259 Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
260 switch (accessFlags) {
261 case ACC_SYNTHETIC:
262 assertTrue(hc.isSynthetic());
263 assertFalse(hc.isEnum());
264 assertFalse(hc.isAnnotation());
265 assertFalse(hc.isInterface());
266 break;
267 case ACC_ENUM:
268 assertFalse(hc.isSynthetic());
269 assertTrue(hc.isEnum());
270 assertFalse(hc.isAnnotation());
271 assertFalse(hc.isInterface());
272 break;
273 case ACC_ABSTRACT:
274 assertFalse(hc.isSynthetic());
275 assertFalse(hc.isEnum());
276 assertFalse(hc.isAnnotation());
277 assertFalse(hc.isInterface());
278 break;
279 case ACC_ABSTRACT|ACC_INTERFACE:
280 assertFalse(hc.isSynthetic());
281 assertFalse(hc.isEnum());
282 assertFalse(hc.isAnnotation());
283 assertTrue(hc.isInterface());
284 break;
285 case ACC_ANNOTATION|ACC_ABSTRACT|ACC_INTERFACE:
286 assertFalse(hc.isSynthetic());
287 assertFalse(hc.isEnum());
288 assertTrue(hc.isAnnotation());
289 assertTrue(hc.isInterface());
290 break;
291 default:
292 throw new IllegalArgumentException("unexpected access flag: " + accessFlags);
293 }
294 assertTrue(hc.isHidden());
295 assertTrue(hc.getModifiers() == (ACC_PUBLIC|accessFlags));
296 assertFalse(hc.isLocalClass());
297 assertFalse(hc.isMemberClass());
298 assertFalse(hc.isAnonymousClass());
299 assertFalse(hc.isArray());
300 }
301
302 // These class files can't be defined as hidden classes
303 @DataProvider(name = "cantBeHiddenClasses")
304 private Object[][] cantBeHiddenClasses() {
305 return new Object[][] {
306 // a hidden class can't be a field's declaring type
307 // enum class with static final HiddenEnum[] $VALUES:
308 new Object[] { "HiddenEnum" },
309 // supertype of this class is a hidden class
310 new Object[] { "HiddenSuper" },
311 // a record class whose equals(HiddenRecord, Object) method
312 // refers to a hidden class in the parameter type and fails
313 // verification. Perhaps this method signature should be reconsidered.
314 new Object[] { "HiddenRecord" },
315 };
316 }
317
318 /*
319 * These class files
320 */
321 @Test(dataProvider = "cantBeHiddenClasses", expectedExceptions = NoClassDefFoundError.class)
322 public void failToDeriveAsHiddenClass(String name) throws Exception {
323 byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve(name + ".class"));
324 Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
325 }
326
327 /*
328 * A hidden class can be successfully created but fails to be reflected
329 * if it refers to its own type in the descriptor.
330 * e.g. Class::getMethods resolves the declaring type of fields,
331 * parameter types and return type.
332 */
333 @Test
334 public void hiddenCantReflect() throws Throwable {
335 HiddenTest t = (HiddenTest)defineHiddenClass("HiddenCantReflect").newInstance();
336 t.test();
337
338 Class<?> c = t.getClass();
339 Class<?>[] intfs = c.getInterfaces();
340 assertTrue(intfs.length == 1);
341 assertTrue(intfs[0] == HiddenTest.class);
342
343 try {
344 // this would cause loading of class HiddenCantReflect and NCDFE due
345 // to error during verification
346 c.getDeclaredMethods();
347 } catch (NoClassDefFoundError e) {
348 Throwable x = e.getCause();
349 if (x == null || !(x instanceof ClassNotFoundException && x.getMessage().contains("HiddenCantReflect"))) {
350 throw e;
351 }
352 }
353 }
354
355 @Test(expectedExceptions = {IllegalArgumentException.class})
356 public void cantDefineModule() throws Throwable {
357 Path src = Paths.get("module-info.java");
358 Path dir = CLASSES_DIR.resolve("m");
359 Files.write(src, List.of("module m {}"), StandardCharsets.UTF_8);
360 compileSources(src, dir);
361
362 byte[] bytes = Files.readAllBytes(dir.resolve("module-info.class"));
363 lookup().defineHiddenClass(bytes, false);
364 }
365
366 @Test(expectedExceptions = {IllegalArgumentException.class})
367 public void cantDefineClassInAnotherPackage() throws Throwable {
368 Path src = Paths.get("ClassInAnotherPackage.java");
369 Files.write(src, List.of("package p;", "public class ClassInAnotherPackage {}"), StandardCharsets.UTF_8);
370 compileSources(src, CLASSES_DIR);
371
372 byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve("p").resolve("ClassInAnotherPackage.class"));
400 // define a hidden class with static nest membership
401 @Test
402 public void hasStaticNestHost() throws Exception {
403 byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve("Outer$Inner.class"));
404 Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
405 hiddenClassWithBadAttribute(hc, "Outer");
406 }
407
408 @Test
409 public void hasStaticNestMembers() throws Throwable {
410 byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve("Outer.class"));
411 Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
412 assertHiddenClass(hc);
413 assertTrue(hc.getNestHost() == hc);
414 Class<?>[] members = hc.getNestMembers();
415 assertTrue(members.length == 1 && members[0] == hc);
416 }
417
418 // a hidden class with bad InnerClasses or EnclosingMethod attribute
419 private void hiddenClassWithBadAttribute(Class<?> hc, String badDeclaringClassName) {
420 assertTrue(hc.isHidden());
421 assertTrue(hc.getCanonicalName() == null);
422 assertTrue(hc.getName().contains("/"));
423
424 if (badDeclaringClassName == null) {
425 // the following reflection API assumes a good name in InnerClasses
426 // or EnclosingMethod attribute can successfully be resolved.
427 assertTrue(hc.getSimpleName().length() > 0);
428 assertFalse(hc.isAnonymousClass());
429 assertFalse(hc.isLocalClass());
430 assertFalse(hc.isMemberClass());
431 } else {
432 declaringClassNotFound(hc, badDeclaringClassName);
433 }
434
435 // validation of nest membership
436 assertTrue(hc.getNestHost() == hc);
437 // validate the static nest membership
438 Class<?>[] members = hc.getNestMembers();
439 assertTrue(members.length == 1 && members[0] == hc);
440 }
451 }
452 }
453 try {
454 // fail to find declaring/enclosing class
455 c.getSimpleName();
456 assertTrue(false);
457 } catch (NoClassDefFoundError e) {
458 if (!e.getMessage().equals(cn)) {
459 throw e;
460 }
461 }
462 }
463
464 private static void singletonNest(Class<?> hc) {
465 assertTrue(hc.getNestHost() == hc);
466 assertTrue(hc.getNestMembers().length == 1);
467 assertTrue(hc.getNestMembers()[0] == hc);
468 }
469
470 private static void assertHiddenClass(Class<?> hc) {
471 assertTrue(hc.isHidden());
472 assertTrue(hc.getCanonicalName() == null);
473 assertTrue(hc.getName().contains("/"));
474 assertFalse(hc.isAnonymousClass());
475 assertFalse(hc.isLocalClass());
476 assertFalse(hc.isMemberClass());
477 assertFalse(hc.getSimpleName().isEmpty()); // sanity check
478 }
479
480 private static byte[] classBytes(String classname, int accessFlags) {
481 return classBytes(classname, Object.class, accessFlags);
482 }
483
484 private static byte[] classBytes(String classname, Class<?> supertType, int accessFlags) {
485 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
486 cw.visit(V14, ACC_PUBLIC|accessFlags, classname, null, Type.getInternalName(supertType), null);
487 cw.visitEnd();
488
489 return cw.toByteArray();
490 }
491 }
|