Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/test/jdk/java/lang/invoke/MethodHandles/classData/ClassDataTest.java
41155 views
1
/*
2
* Copyright (c) 2020, 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
/*
25
* @test
26
* @bug 8230501
27
* @library /test/lib
28
* @modules java.base/jdk.internal.org.objectweb.asm
29
* @run testng/othervm ClassDataTest
30
*/
31
32
import java.io.IOException;
33
import java.io.OutputStream;
34
import java.io.UncheckedIOException;
35
import java.lang.invoke.MethodHandle;
36
import java.lang.invoke.MethodHandles;
37
import java.lang.invoke.MethodHandles.Lookup;
38
import java.lang.invoke.MethodType;
39
import java.lang.reflect.Method;
40
import java.nio.file.Files;
41
import java.nio.file.Path;
42
import java.nio.file.Paths;
43
import java.util.ArrayList;
44
import java.util.List;
45
import java.util.Map;
46
import java.util.stream.Stream;
47
48
import jdk.internal.org.objectweb.asm.*;
49
import org.testng.annotations.DataProvider;
50
import org.testng.annotations.Test;
51
52
import static java.lang.invoke.MethodHandles.Lookup.*;
53
import static jdk.internal.org.objectweb.asm.Opcodes.*;
54
import static org.testng.Assert.*;
55
56
public class ClassDataTest {
57
private static final Lookup LOOKUP = MethodHandles.lookup();
58
59
@Test
60
public void testOriginalAccess() throws IllegalAccessException {
61
Lookup lookup = hiddenClass(20);
62
assertTrue(lookup.hasFullPrivilegeAccess());
63
64
int value = MethodHandles.classData(lookup, "_", int.class);
65
assertEquals(value, 20);
66
67
Integer i = MethodHandles.classData(lookup, "_", Integer.class);
68
assertEquals(i.intValue(), 20);
69
}
70
71
/*
72
* A lookup class with no class data.
73
*/
74
@Test
75
public void noClassData() throws IllegalAccessException {
76
assertNull(MethodHandles.classData(LOOKUP, "_", Object.class));
77
}
78
79
@DataProvider(name = "teleportedLookup")
80
private Object[][] teleportedLookup() throws ReflectiveOperationException {
81
Lookup lookup = hiddenClass(30);
82
Class<?> hc = lookup.lookupClass();
83
assertClassData(lookup, 30);
84
85
int fullAccess = PUBLIC|PROTECTED|PACKAGE|MODULE|PRIVATE;
86
return new Object[][] {
87
new Object[] { MethodHandles.privateLookupIn(hc, LOOKUP), fullAccess},
88
new Object[] { LOOKUP.in(hc), fullAccess & ~(PROTECTED|PRIVATE) },
89
new Object[] { lookup.dropLookupMode(PRIVATE), fullAccess & ~(PROTECTED|PRIVATE) },
90
};
91
}
92
93
@Test(dataProvider = "teleportedLookup", expectedExceptions = { IllegalAccessException.class })
94
public void illegalAccess(Lookup lookup, int access) throws IllegalAccessException {
95
int lookupModes = lookup.lookupModes();
96
assertTrue((lookupModes & ORIGINAL) == 0);
97
assertEquals(lookupModes, access);
98
MethodHandles.classData(lookup, "_", int.class);
99
}
100
101
@Test(expectedExceptions = { ClassCastException.class })
102
public void incorrectType() throws IllegalAccessException {
103
Lookup lookup = hiddenClass(20);
104
MethodHandles.classData(lookup, "_", Long.class);
105
}
106
107
@Test(expectedExceptions = { IndexOutOfBoundsException.class })
108
public void invalidIndex() throws IllegalAccessException {
109
Lookup lookup = hiddenClass(List.of());
110
MethodHandles.classDataAt(lookup, "_", Object.class, 0);
111
}
112
113
@Test(expectedExceptions = { NullPointerException.class })
114
public void unboxNull() throws IllegalAccessException {
115
List<Integer> list = new ArrayList<>();
116
list.add(null);
117
Lookup lookup = hiddenClass(list);
118
MethodHandles.classDataAt(lookup, "_", int.class, 0);
119
}
120
121
@Test
122
public void nullElement() throws IllegalAccessException {
123
List<Object> list = new ArrayList<>();
124
list.add(null);
125
Lookup lookup = hiddenClass(list);
126
assertTrue(MethodHandles.classDataAt(lookup, "_", Object.class, 0) == null);
127
}
128
129
@Test
130
public void intClassData() throws ReflectiveOperationException {
131
ClassByteBuilder builder = new ClassByteBuilder("T1-int");
132
byte[] bytes = builder.classData(ACC_PUBLIC|ACC_STATIC, int.class).build();
133
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, 100, true);
134
int value = MethodHandles.classData(lookup, "_", int.class);
135
assertEquals(value, 100);
136
// call through condy
137
assertClassData(lookup, 100);
138
}
139
140
@Test
141
public void floatClassData() throws ReflectiveOperationException {
142
ClassByteBuilder builder = new ClassByteBuilder("T1-float");
143
byte[] bytes = builder.classData(ACC_PUBLIC|ACC_STATIC, float.class).build();
144
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, 0.1234f, true);
145
float value = MethodHandles.classData(lookup, "_", float.class);
146
assertEquals(value, 0.1234f);
147
// call through condy
148
assertClassData(lookup, 0.1234f);
149
}
150
151
@Test
152
public void classClassData() throws ReflectiveOperationException {
153
Class<?> hc = hiddenClass(100).lookupClass();
154
ClassByteBuilder builder = new ClassByteBuilder("T2");
155
byte[] bytes = builder.classData(ACC_PUBLIC|ACC_STATIC, Class.class).build();
156
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, hc, true);
157
Class<?> value = MethodHandles.classData(lookup, "_", Class.class);
158
assertEquals(value, hc);
159
// call through condy
160
assertClassData(lookup, hc);
161
}
162
163
@Test
164
public void arrayClassData() throws ReflectiveOperationException {
165
ClassByteBuilder builder = new ClassByteBuilder("T3");
166
byte[] bytes = builder.classData(ACC_PUBLIC|ACC_STATIC, String[].class).build();
167
String[] colors = new String[] { "red", "yellow", "blue"};
168
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, colors, true);
169
assertClassData(lookup, colors.clone());
170
// class data is modifiable and not a constant
171
colors[0] = "black";
172
// it will get back the modified class data
173
String[] value = MethodHandles.classData(lookup, "_", String[].class);
174
assertEquals(value, colors);
175
// even call through condy as it's not a constant
176
assertClassData(lookup, colors);
177
}
178
179
@Test
180
public void listClassData() throws ReflectiveOperationException {
181
ClassByteBuilder builder = new ClassByteBuilder("T4");
182
byte[] bytes = builder.classDataAt(ACC_PUBLIC|ACC_STATIC, Integer.class, 2).build();
183
List<Integer> cd = List.of(100, 101, 102, 103);
184
int expected = 102; // element at index=2
185
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, cd, true);
186
int value = MethodHandles.classDataAt(lookup, "_", int.class, 2);
187
assertEquals(value, expected);
188
// call through condy
189
assertClassData(lookup, expected);
190
}
191
192
@Test
193
public void arrayListClassData() throws ReflectiveOperationException {
194
ClassByteBuilder builder = new ClassByteBuilder("T4");
195
byte[] bytes = builder.classDataAt(ACC_PUBLIC|ACC_STATIC, Integer.class, 1).build();
196
ArrayList<Integer> cd = new ArrayList<>();
197
Stream.of(100, 101, 102, 103).forEach(cd::add);
198
int expected = 101; // element at index=1
199
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, cd, true);
200
int value = MethodHandles.classDataAt(lookup, "_", int.class, 1);
201
assertEquals(value, expected);
202
// call through condy
203
assertClassData(lookup, expected);
204
}
205
206
private static Lookup hiddenClass(int value) {
207
ClassByteBuilder builder = new ClassByteBuilder("HC");
208
byte[] bytes = builder.classData(ACC_PUBLIC|ACC_STATIC, int.class).build();
209
try {
210
return LOOKUP.defineHiddenClassWithClassData(bytes, value, true);
211
} catch (Throwable e) {
212
throw new RuntimeException(e);
213
}
214
}
215
private static Lookup hiddenClass(List<?> list) {
216
ClassByteBuilder builder = new ClassByteBuilder("HC");
217
byte[] bytes = builder.classData(ACC_PUBLIC|ACC_STATIC, List.class).build();
218
try {
219
return LOOKUP.defineHiddenClassWithClassData(bytes, list, true);
220
} catch (Throwable e) {
221
throw new RuntimeException(e);
222
}
223
}
224
225
@Test
226
public void condyInvokedFromVirtualMethod() throws ReflectiveOperationException {
227
ClassByteBuilder builder = new ClassByteBuilder("T5");
228
// generate classData instance method
229
byte[] bytes = builder.classData(ACC_PUBLIC, Class.class).build();
230
Lookup hcLookup = hiddenClass(100);
231
assertClassData(hcLookup, 100);
232
Class<?> hc = hcLookup.lookupClass();
233
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, hc, true);
234
Class<?> value = MethodHandles.classData(lookup, "_", Class.class);
235
assertEquals(value, hc);
236
// call through condy
237
Class<?> c = lookup.lookupClass();
238
assertClassData(lookup, c.newInstance(), hc);
239
}
240
241
@Test
242
public void immutableListClassData() throws ReflectiveOperationException {
243
ClassByteBuilder builder = new ClassByteBuilder("T6");
244
// generate classDataAt instance method
245
byte[] bytes = builder.classDataAt(ACC_PUBLIC, Integer.class, 2).build();
246
List<Integer> cd = List.of(100, 101, 102, 103);
247
int expected = 102; // element at index=2
248
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, cd, true);
249
int value = MethodHandles.classDataAt(lookup, "_", int.class, 2);
250
assertEquals(value, expected);
251
// call through condy
252
Class<?> c = lookup.lookupClass();
253
assertClassData(lookup, c.newInstance() ,expected);
254
}
255
256
/*
257
* The return value of MethodHandles::classDataAt is the element
258
* contained in the list when the method is called.
259
* If MethodHandles::classDataAt is called via condy, the value
260
* will be captured as a constant. If the class data is modified
261
* after the element at the given index is computed via condy,
262
* subsequent LDC of such ConstantDynamic entry will return the same
263
* value. However, direct invocation of MethodHandles::classDataAt
264
* will return the modified value.
265
*/
266
@Test
267
public void mutableListClassData() throws ReflectiveOperationException {
268
ClassByteBuilder builder = new ClassByteBuilder("T7");
269
// generate classDataAt instance method
270
byte[] bytes = builder.classDataAt(ACC_PUBLIC, MethodType.class, 0).build();
271
MethodType mtype = MethodType.methodType(int.class, String.class);
272
List<MethodType> cd = new ArrayList<>(List.of(mtype));
273
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, cd, true);
274
// call through condy
275
Class<?> c = lookup.lookupClass();
276
assertClassData(lookup, c.newInstance(), mtype);
277
// modify the class data
278
assertTrue(cd.remove(0) == mtype);
279
cd.add(0, MethodType.methodType(void.class));
280
MethodType newMType = cd.get(0);
281
// loading the element using condy returns the original value
282
assertClassData(lookup, c.newInstance(), mtype);
283
// direct invocation of MethodHandles.classDataAt returns the modified value
284
assertEquals(MethodHandles.classDataAt(lookup, "_", MethodType.class, 0), newMType);
285
}
286
287
// helper method to extract from a class data map
288
public static <T> T getClassDataEntry(Lookup lookup, String key, Class<T> type) throws IllegalAccessException {
289
Map<String, T> cd = MethodHandles.classData(lookup, "_", Map.class);
290
return type.cast(cd.get(key));
291
}
292
293
@Test
294
public void classDataMap() throws ReflectiveOperationException {
295
ClassByteBuilder builder = new ClassByteBuilder("map");
296
// generate classData static method
297
Handle bsm = new Handle(H_INVOKESTATIC, "ClassDataTest", "getClassDataEntry",
298
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;",
299
false);
300
// generate two accessor methods to get the entries from class data
301
byte[] bytes = builder.classData(ACC_PUBLIC|ACC_STATIC, Map.class)
302
.classData(ACC_PUBLIC|ACC_STATIC, "getClass",
303
Class.class, new ConstantDynamic("class", Type.getDescriptor(Class.class), bsm))
304
.classData(ACC_PUBLIC|ACC_STATIC, "getMethod",
305
MethodHandle.class, new ConstantDynamic("method", Type.getDescriptor(MethodHandle.class), bsm))
306
.build();
307
308
// generate a hidden class
309
Lookup hcLookup = hiddenClass(100);
310
Class<?> hc = hcLookup.lookupClass();
311
assertClassData(hcLookup, 100);
312
313
MethodHandle mh = hcLookup.findStatic(hc, "classData", MethodType.methodType(int.class));
314
Map<String, Object> cd = Map.of("class", hc, "method", mh);
315
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, cd, true);
316
assertClassData(lookup, cd);
317
318
// validate the entries from the class data map
319
Class<?> c = lookup.lookupClass();
320
Method m = c.getMethod("getClass");
321
Class<?> v = (Class<?>)m.invoke(null);
322
assertEquals(hc, v);
323
324
Method m1 = c.getMethod("getMethod");
325
MethodHandle v1 = (MethodHandle) m1.invoke(null);
326
assertEquals(mh, v1);
327
}
328
329
@Test(expectedExceptions = { IllegalArgumentException.class })
330
public void nonDefaultName() throws ReflectiveOperationException {
331
ClassByteBuilder builder = new ClassByteBuilder("nonDefaultName");
332
byte[] bytes = builder.classData(ACC_PUBLIC|ACC_STATIC, Class.class)
333
.build();
334
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, ClassDataTest.class, true);
335
assertClassData(lookup, ClassDataTest.class);
336
// throw IAE
337
MethodHandles.classData(lookup, "non_default_name", Class.class);
338
}
339
340
static class ClassByteBuilder {
341
private static final String OBJECT_CLS = "java/lang/Object";
342
private static final String MHS_CLS = "java/lang/invoke/MethodHandles";
343
private static final String CLASS_DATA_BSM_DESCR =
344
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;";
345
private final ClassWriter cw;
346
private final String classname;
347
348
/**
349
* A builder to generate a class file to access class data
350
* @param classname
351
*/
352
ClassByteBuilder(String classname) {
353
this.classname = classname;
354
this.cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
355
cw.visit(V14, ACC_FINAL, classname, null, OBJECT_CLS, null);
356
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
357
mv.visitCode();
358
mv.visitVarInsn(ALOAD, 0);
359
mv.visitMethodInsn(INVOKESPECIAL, OBJECT_CLS, "<init>", "()V", false);
360
mv.visitInsn(RETURN);
361
mv.visitMaxs(0, 0);
362
mv.visitEnd();
363
}
364
365
byte[] build() {
366
cw.visitEnd();
367
byte[] bytes = cw.toByteArray();
368
Path p = Paths.get(classname + ".class");
369
try (OutputStream os = Files.newOutputStream(p)) {
370
os.write(bytes);
371
} catch (IOException e) {
372
throw new UncheckedIOException(e);
373
}
374
return bytes;
375
}
376
377
/*
378
* Generate classData method to load class data via condy
379
*/
380
ClassByteBuilder classData(int accessFlags, Class<?> returnType) {
381
MethodType mtype = MethodType.methodType(returnType);
382
MethodVisitor mv = cw.visitMethod(accessFlags,
383
"classData",
384
mtype.descriptorString(), null, null);
385
mv.visitCode();
386
Handle bsm = new Handle(H_INVOKESTATIC, MHS_CLS, "classData",
387
CLASS_DATA_BSM_DESCR,
388
false);
389
ConstantDynamic dynamic = new ConstantDynamic("_", Type.getDescriptor(returnType), bsm);
390
mv.visitLdcInsn(dynamic);
391
mv.visitInsn(returnType == int.class ? IRETURN :
392
(returnType == float.class ? FRETURN : ARETURN));
393
mv.visitMaxs(0, 0);
394
mv.visitEnd();
395
return this;
396
}
397
398
/*
399
* Generate classDataAt method to load an element from class data via condy
400
*/
401
ClassByteBuilder classDataAt(int accessFlags, Class<?> returnType, int index) {
402
MethodType mtype = MethodType.methodType(returnType);
403
MethodVisitor mv = cw.visitMethod(accessFlags,
404
"classData",
405
mtype.descriptorString(), null, null);
406
mv.visitCode();
407
Handle bsm = new Handle(H_INVOKESTATIC, "java/lang/invoke/MethodHandles", "classDataAt",
408
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;I)Ljava/lang/Object;",
409
false);
410
ConstantDynamic dynamic = new ConstantDynamic("_", Type.getDescriptor(returnType), bsm, index);
411
mv.visitLdcInsn(dynamic);
412
mv.visitInsn(returnType == int.class? IRETURN : ARETURN);
413
mv.visitMaxs(0, 0);
414
mv.visitEnd();
415
return this;
416
}
417
418
ClassByteBuilder classData(int accessFlags, String name, Class<?> returnType, ConstantDynamic dynamic) {
419
MethodType mtype = MethodType.methodType(returnType);
420
MethodVisitor mv = cw.visitMethod(accessFlags,
421
name,
422
mtype.descriptorString(), null, null);
423
mv.visitCode();
424
mv.visitLdcInsn(dynamic);
425
mv.visitInsn(returnType == int.class? IRETURN : ARETURN);
426
mv.visitMaxs(0, 0);
427
mv.visitEnd();
428
return this;
429
}
430
}
431
432
/*
433
* Load an int constant from class data via condy and
434
* verify it matches the given value.
435
*/
436
private void assertClassData(Lookup lookup, int value) throws ReflectiveOperationException {
437
Class<?> c = lookup.lookupClass();
438
Method m = c.getMethod("classData");
439
int v = (int) m.invoke(null);
440
assertEquals(value, v);
441
}
442
443
/*
444
* Load an int constant from class data via condy and
445
* verify it matches the given value.
446
*/
447
private void assertClassData(Lookup lookup, Object o, int value) throws ReflectiveOperationException {
448
Class<?> c = lookup.lookupClass();
449
Method m = c.getMethod("classData");
450
int v = (int) m.invoke(o);
451
assertEquals(value, v);
452
}
453
454
/*
455
* Load a float constant from class data via condy and
456
* verify it matches the given value.
457
*/
458
private void assertClassData(Lookup lookup, float value) throws ReflectiveOperationException {
459
Class<?> c = lookup.lookupClass();
460
Method m = c.getMethod("classData");
461
float v = (float) m.invoke(null);
462
assertEquals(value, v);
463
}
464
465
/*
466
* Load a Class constant from class data via condy and
467
* verify it matches the given value.
468
*/
469
private void assertClassData(Lookup lookup, Class<?> value) throws ReflectiveOperationException {
470
Class<?> c = lookup.lookupClass();
471
Method m = c.getMethod("classData");
472
Class<?> v = (Class<?>)m.invoke(null);
473
assertEquals(value, v);
474
}
475
476
/*
477
* Load a Class from class data via condy and
478
* verify it matches the given value.
479
*/
480
private void assertClassData(Lookup lookup, Object o, Class<?> value) throws ReflectiveOperationException {
481
Class<?> c = lookup.lookupClass();
482
Method m = c.getMethod("classData");
483
Object v = m.invoke(o);
484
assertEquals(value, v);
485
}
486
487
/*
488
* Load an Object from class data via condy and
489
* verify it matches the given value.
490
*/
491
private void assertClassData(Lookup lookup, Object value) throws ReflectiveOperationException {
492
Class<?> c = lookup.lookupClass();
493
Method m = c.getMethod("classData");
494
Object v = m.invoke(null);
495
assertEquals(value, v);
496
}
497
498
/*
499
* Load an Object from class data via condy and
500
* verify it matches the given value.
501
*/
502
private void assertClassData(Lookup lookup, Object o, Object value) throws ReflectiveOperationException {
503
Class<?> c = lookup.lookupClass();
504
Method m = c.getMethod("classData");
505
Object v = m.invoke(o);
506
assertEquals(value, v);
507
}
508
}
509
510
511
512