1 /*
2 * Copyright (c) 2012, 2021, 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 * @requires vm.jvmci
27 * @library ../../../../../
28 * @modules java.base/jdk.internal.org.objectweb.asm
29 * java.base/jdk.internal.reflect
30 * jdk.internal.vm.ci/jdk.vm.ci.meta
31 * jdk.internal.vm.ci/jdk.vm.ci.runtime
32 * jdk.internal.vm.ci/jdk.vm.ci.common
33 * java.base/jdk.internal.misc
34 * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:-UseJVMCICompiler jdk.vm.ci.runtime.test.TestResolvedJavaType
35 */
36
37 package jdk.vm.ci.runtime.test;
38
39 import static java.lang.reflect.Modifier.isAbstract;
40 import static java.lang.reflect.Modifier.isFinal;
41 import static java.lang.reflect.Modifier.isPrivate;
42 import static java.lang.reflect.Modifier.isProtected;
43 import static java.lang.reflect.Modifier.isPublic;
44 import static java.lang.reflect.Modifier.isStatic;
45 import static jdk.vm.ci.meta.MetaUtil.internalNameToJava;
46 import static jdk.vm.ci.meta.MetaUtil.toInternalName;
47 import static org.junit.Assert.assertArrayEquals;
48 import static org.junit.Assert.assertEquals;
49 import static org.junit.Assert.assertFalse;
50 import static org.junit.Assert.assertNotNull;
51 import static org.junit.Assert.assertNull;
52 import static org.junit.Assert.assertTrue;
53
54 import java.io.DataInputStream;
55 import java.io.IOException;
56 import java.io.InputStream;
57 import java.lang.annotation.Annotation;
58 import java.lang.invoke.MethodHandles.Lookup;
59 import java.lang.reflect.AccessibleObject;
60 import java.lang.reflect.Constructor;
61 import java.lang.reflect.Field;
62 import java.lang.reflect.Method;
63 import java.lang.reflect.Modifier;
64 import java.util.Arrays;
65 import java.util.Collections;
66 import java.util.function.Supplier;
67 import java.util.HashMap;
68 import java.util.HashSet;
69 import java.util.Map;
70 import java.util.Set;
71
72 import org.junit.Assert;
73 import org.junit.Test;
74
75 import jdk.internal.org.objectweb.asm.*;
76 import jdk.internal.reflect.ConstantPool;
77 import jdk.vm.ci.common.JVMCIError;
78 import jdk.vm.ci.meta.Assumptions.AssumptionResult;
79 import jdk.vm.ci.meta.JavaConstant;
80 import jdk.vm.ci.meta.JavaKind;
81 import jdk.vm.ci.meta.ResolvedJavaField;
82 import jdk.vm.ci.meta.ResolvedJavaMethod;
83 import jdk.vm.ci.meta.ResolvedJavaType;
84
85 /**
86 * Tests for {@link ResolvedJavaType}.
87 */
88 public class TestResolvedJavaType extends TypeUniverse {
89 private static final Class<? extends Annotation> SIGNATURE_POLYMORPHIC_CLASS = findPolymorphicSignatureClass();
90
91 public TestResolvedJavaType() {
92 }
93
94 @SuppressWarnings("unchecked")
95 private static Class<? extends Annotation> findPolymorphicSignatureClass() {
96 Class<? extends Annotation> signaturePolyAnnotation = null;
97 try {
98 for (Class<?> clazz : TestResolvedJavaType.class.getClassLoader().loadClass("java.lang.invoke.MethodHandle").getDeclaredClasses()) {
99 if (clazz.getName().endsWith("PolymorphicSignature") && Annotation.class.isAssignableFrom(clazz)) {
100 signaturePolyAnnotation = (Class<? extends Annotation>) clazz;
101 break;
102 }
103 }
104 } catch (Throwable e) {
105 throw new AssertionError("Could not find annotation PolymorphicSignature in java.lang.invoke.MethodHandle", e);
106 }
107 assertNotNull(signaturePolyAnnotation);
108 return signaturePolyAnnotation;
109 }
110
111 @Test
112 public void findInstanceFieldWithOffsetTest() {
113 for (Class<?> c : classes) {
114 ResolvedJavaType type = metaAccess.lookupJavaType(c);
115 Set<Field> reflectionFields = getInstanceFields(c, true);
116 for (Field f : reflectionFields) {
117 ResolvedJavaField rf = lookupField(type.getInstanceFields(true), f);
118 assertNotNull(rf);
119 long offset = isStatic(f.getModifiers()) ? unsafe.staticFieldOffset(f) : unsafe.objectFieldOffset(f);
120 ResolvedJavaField result = type.findInstanceFieldWithOffset(offset, rf.getJavaKind());
121 assertNotNull(result);
122 assertTrue(fieldsEqual(f, result));
123 }
124 }
125 }
126
127 @Test
128 public void isInterfaceTest() {
129 for (Class<?> c : classes) {
130 ResolvedJavaType type = metaAccess.lookupJavaType(c);
131 boolean expected = c.isInterface();
132 boolean actual = type.isInterface();
133 assertEquals(expected, actual);
134 }
135 }
136
137 @Test
138 public void isEnumTest() {
139 for (Class<?> c : classes) {
140 ResolvedJavaType type = metaAccess.lookupJavaType(c);
141 boolean expected = c.isEnum();
142 boolean actual = type.isEnum();
143 assertEquals(expected, actual);
144 }
145 }
146
147 @Test
148 public void isInstanceClassTest() {
149 for (Class<?> c : classes) {
150 ResolvedJavaType type = metaAccess.lookupJavaType(c);
151 boolean expected = !c.isArray() && !c.isPrimitive() && !c.isInterface();
152 boolean actual = type.isInstanceClass();
153 assertEquals(expected, actual);
154 }
155 }
156
157 @Test
158 public void isArrayTest() {
159 for (Class<?> c : classes) {
160 ResolvedJavaType type = metaAccess.lookupJavaType(c);
161 boolean expected = c.isArray();
162 boolean actual = type.isArray();
163 assertEquals(expected, actual);
164 }
165 }
166
167 @Test
168 public void lambdaInternalNameTest() {
169 // Verify that the last dot in lambda types is properly handled when transitioning from internal name to java
170 // name and vice versa.
171 Supplier<Runnable> lambda = () -> () -> System.out.println("run");
172 ResolvedJavaType lambdaType = metaAccess.lookupJavaType(lambda.getClass());
173 String typeName = lambdaType.getName();
174 String javaName = lambda.getClass().getName();
175 assertEquals(typeName, toInternalName(javaName));
176 assertEquals(javaName, internalNameToJava(typeName, true, true));
177 }
178
179 @Test
180 public void getModifiersTest() {
181 for (Class<?> c : classes) {
182 ResolvedJavaType type = metaAccess.lookupJavaType(c);
183 int mask = Modifier.classModifiers() & ~Modifier.STATIC;
184 int expected = c.getModifiers() & mask;
185 int actual = type.getModifiers() & mask;
186 Class<?> elementalType = c;
187 while (elementalType.isArray()) {
188 elementalType = elementalType.getComponentType();
189 }
190 if (elementalType.isMemberClass()) {
191 // member class get their modifiers from the inner-class attribute in the JVM and
192 // from the classfile header in jvmci
193 expected &= ~(Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED);
194 actual &= ~(Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED);
195 }
196 assertEquals(String.format("%s: 0x%x != 0x%x", type, expected, actual), expected, actual);
197 }
198 }
199
200 @Test
201 public void isAssignableFromTest() {
202 Class<?>[] all = classes.toArray(new Class<?>[classes.size()]);
203 for (int i = 0; i < all.length; i++) {
204 Class<?> c1 = all[i];
205 for (int j = i; j < all.length; j++) {
206 Class<?> c2 = all[j];
207 ResolvedJavaType t1 = metaAccess.lookupJavaType(c1);
208 ResolvedJavaType t2 = metaAccess.lookupJavaType(c2);
209 boolean expected = c1.isAssignableFrom(c2);
210 boolean actual = t1.isAssignableFrom(t2);
211 assertEquals(expected, actual);
212 if (expected && t1 != t2) {
213 assertFalse(t2.isAssignableFrom(t1));
214 }
215 }
216 }
217 }
218
219 @Test
220 public void isInstanceTest() {
221 for (ConstantValue cv : constants()) {
222 JavaConstant c = cv.value;
223 if (c.getJavaKind() == JavaKind.Object && !c.isNull()) {
224 ResolvedJavaType cType = metaAccess.lookupJavaType(c);
225 for (ResolvedJavaType t : javaTypes) {
226 if (t.isAssignableFrom(cType)) {
227 assertTrue(t.isInstance(c));
228 } else {
229 assertFalse(t.isInstance(c));
230 }
231 }
232 }
233 }
234 }
235
236 @Test
237 public void getSuperclassTest() {
238 for (Class<?> c : classes) {
239 ResolvedJavaType type = metaAccess.lookupJavaType(c);
240 Class<?> expected = c.getSuperclass();
241 ResolvedJavaType actual = type.getSuperclass();
242 if (expected == null) {
243 assertTrue(actual == null);
244 } else {
245 assertNotNull(actual);
246 assertTrue(actual.equals(metaAccess.lookupJavaType(expected)));
247 }
248 }
249 }
250
251 @Test
252 public void getInterfacesTest() {
253 for (Class<?> c : classes) {
254 ResolvedJavaType type = metaAccess.lookupJavaType(c);
255 Class<?>[] expected = c.getInterfaces();
256 ResolvedJavaType[] actual = type.getInterfaces();
257 assertEquals(expected.length, actual.length);
258 for (int i = 0; i < expected.length; i++) {
259 assertTrue(actual[i].equals(metaAccess.lookupJavaType(expected[i])));
260 }
261 }
262 }
263
264 public Class<?> getSupertype(Class<?> c) {
265 assert !c.isPrimitive();
266 if (c.isArray()) {
267 Class<?> componentType = c.getComponentType();
268 if (componentType.isPrimitive() || componentType == Object.class) {
269 return Object.class;
270 }
271 return getArrayClass(getSupertype(componentType));
272 }
273 if (c.isInterface()) {
274 return Object.class;
275 }
276 return c.getSuperclass();
277 }
278
279 public Class<?> findLeastCommonAncestor(Class<?> c1Initial, Class<?> c2Initial) {
280 if (c1Initial.isPrimitive() || c2Initial.isPrimitive()) {
281 return null;
282 } else {
283 Class<?> c1 = c1Initial;
284 Class<?> c2 = c2Initial;
285 while (true) {
286 if (c1.isAssignableFrom(c2)) {
287 return c1;
288 }
289 if (c2.isAssignableFrom(c1)) {
290 return c2;
291 }
292 c1 = getSupertype(c1);
293 c2 = getSupertype(c2);
294 }
295 }
296 }
297
298 @Test
299 public void findLeastCommonAncestorTest() {
300 Class<?>[] all = classes.toArray(new Class<?>[classes.size()]);
301 for (int i = 0; i < all.length; i++) {
302 Class<?> c1 = all[i];
303 for (int j = i; j < all.length; j++) {
304 Class<?> c2 = all[j];
305 ResolvedJavaType t1 = metaAccess.lookupJavaType(c1);
306 ResolvedJavaType t2 = metaAccess.lookupJavaType(c2);
307 Class<?> expected = findLeastCommonAncestor(c1, c2);
308 ResolvedJavaType actual = t1.findLeastCommonAncestor(t2);
309 if (expected == null) {
310 assertTrue(actual == null);
311 } else {
312 assertNotNull(actual);
313 assertTrue(actual.equals(metaAccess.lookupJavaType(expected)));
314 }
315 }
316 }
317 }
318
319 @Test
320 public void linkTest() {
321 for (Class<?> c : classes) {
322 ResolvedJavaType type = metaAccess.lookupJavaType(c);
323 type.link();
324 }
325 }
326
327 private class HidingClassLoader extends ClassLoader {
328 @Override
329 protected Class<?> findClass(final String name) throws ClassNotFoundException {
330 if (name.endsWith("MissingInterface")) {
331 throw new ClassNotFoundException("missing");
332 }
333 byte[] classData = null;
334 try {
335 InputStream is = HidingClassLoader.class.getResourceAsStream("/" + name.replace('.', '/') + ".class");
336 classData = new byte[is.available()];
337 new DataInputStream(is).readFully(classData);
338 } catch (IOException e) {
339 Assert.fail("can't access class: " + name);
340 }
341
342 return defineClass(null, classData, 0, classData.length);
343 }
344
345 ResolvedJavaType lookupJavaType(String name) throws ClassNotFoundException {
346 return metaAccess.lookupJavaType(loadClass(name));
347 }
348
349 HidingClassLoader() {
350 super(null);
351 }
352
353 }
354
355 interface MissingInterface {
356 }
357
358 static class MissingInterfaceImpl implements MissingInterface {
359 }
360
361 interface SomeInterface {
362 default MissingInterface someMethod() {
363 return new MissingInterfaceImpl();
364 }
365 }
366
367 static class Wrapper implements SomeInterface {
368 }
369
370 @Test
371 public void linkExceptionTest() throws ClassNotFoundException {
372 HidingClassLoader cl = new HidingClassLoader();
373 ResolvedJavaType inner = cl.lookupJavaType(Wrapper.class.getName());
374 assertTrue("expected default methods", inner.hasDefaultMethods());
375 try {
376 inner.link();
377 assertFalse("link should throw an exception", true);
378 } catch (NoClassDefFoundError e) {
379 }
380 }
381
382 @Test
383 public void hasDefaultMethodsTest() {
384 for (Class<?> c : classes) {
385 ResolvedJavaType type = metaAccess.lookupJavaType(c);
386 assertEquals(hasDefaultMethods(type), type.hasDefaultMethods());
387 }
388 }
389
390 @Test
391 public void declaresDefaultMethodsTest() {
392 for (Class<?> c : classes) {
393 ResolvedJavaType type = metaAccess.lookupJavaType(c);
394 assertEquals(declaresDefaultMethods(type), type.declaresDefaultMethods());
395 }
396 }
397
398 private static boolean hasDefaultMethods(ResolvedJavaType type) {
399 if (!type.isInterface() && type.getSuperclass() != null && hasDefaultMethods(type.getSuperclass())) {
400 return true;
401 }
402 for (ResolvedJavaType iface : type.getInterfaces()) {
403 if (hasDefaultMethods(iface)) {
404 return true;
405 }
406 }
407 return declaresDefaultMethods(type);
408 }
409
410 static boolean declaresDefaultMethods(ResolvedJavaType type) {
411 if (!type.isInterface()) {
412 /* Only interfaces can declare default methods. */
413 return false;
414 }
415 for (ResolvedJavaMethod method : type.getDeclaredMethods()) {
416 if (method.isDefault()) {
417 assert !Modifier.isStatic(method.getModifiers()) : "Default method that is static?";
418 return true;
419 }
420 }
421 return false;
422 }
423
424 private static class Base {
425 }
426
427 abstract static class Abstract1 extends Base {
428 }
429
430 interface Interface1 {
431 }
432
433 static class Concrete1 extends Abstract1 {
434 }
435
436 static class Concrete2 extends Abstract1 implements Interface1 {
437 }
438
439 static class Concrete3 extends Concrete2 {
440 }
441
442 static final class Final1 extends Abstract1 {
443 }
444
445 abstract static class Abstract4 extends Concrete3 {
446 }
447
448 void checkConcreteSubtype(ResolvedJavaType type, ResolvedJavaType expected) {
449 AssumptionResult<ResolvedJavaType> leafConcreteSubtype = type.findLeafConcreteSubtype();
450 if (leafConcreteSubtype == null) {
451 // findLeafConcreteSubtype() is conservative
452 } else {
453 if (expected == null) {
454 assertNull(leafConcreteSubtype);
455 } else {
456 assertTrue(leafConcreteSubtype.getResult().equals(expected));
457 }
458 assertTrue(!type.isLeaf() || leafConcreteSubtype.isAssumptionFree());
459 }
460
461 if (!type.isArray()) {
462 ResolvedJavaType arrayType = type.getArrayClass();
463 AssumptionResult<ResolvedJavaType> arraySubtype = arrayType.findLeafConcreteSubtype();
464 if (arraySubtype != null) {
465 assertEquals(arraySubtype.getResult(), arrayType);
466 } else {
467 // findLeafConcreteSubtype() method is conservative
468 }
469 }
470 }
471
472 @Test
473 public void findLeafConcreteSubtypeTest() {
474 ResolvedJavaType base = metaAccess.lookupJavaType(Base.class);
475 checkConcreteSubtype(base, base);
476
477 ResolvedJavaType a1 = metaAccess.lookupJavaType(Abstract1.class);
478 ResolvedJavaType c1 = metaAccess.lookupJavaType(Concrete1.class);
479
480 checkConcreteSubtype(base, null);
481 checkConcreteSubtype(a1, c1);
482 checkConcreteSubtype(c1, c1);
483
484 ResolvedJavaType i1 = metaAccess.lookupJavaType(Interface1.class);
485 ResolvedJavaType c2 = metaAccess.lookupJavaType(Concrete2.class);
486
487 checkConcreteSubtype(base, null);
488 checkConcreteSubtype(a1, null);
489 checkConcreteSubtype(c1, c1);
490 checkConcreteSubtype(i1, c2);
491 checkConcreteSubtype(c2, c2);
492
493 ResolvedJavaType c3 = metaAccess.lookupJavaType(Concrete3.class);
494 checkConcreteSubtype(c2, null);
495 checkConcreteSubtype(c3, c3);
496
497 ResolvedJavaType a4 = metaAccess.lookupJavaType(Abstract4.class);
498 checkConcreteSubtype(c3, null);
499 checkConcreteSubtype(a4, null);
500
501 ResolvedJavaType a1a = metaAccess.lookupJavaType(Abstract1[].class);
502 checkConcreteSubtype(a1a, null);
503 ResolvedJavaType i1a = metaAccess.lookupJavaType(Interface1[].class);
504 checkConcreteSubtype(i1a, null);
505 ResolvedJavaType c1a = metaAccess.lookupJavaType(Concrete1[].class);
506 checkConcreteSubtype(c1a, c1a);
507 ResolvedJavaType f1a = metaAccess.lookupJavaType(Final1[].class);
508 checkConcreteSubtype(f1a, f1a);
509
510 ResolvedJavaType obja = metaAccess.lookupJavaType(Object[].class);
511 checkConcreteSubtype(obja, null);
512
513 ResolvedJavaType inta = metaAccess.lookupJavaType(int[].class);
514 checkConcreteSubtype(inta, inta);
515 }
516
517 interface NoImplementor {
518 }
519
520 interface SingleImplementorInterface {
521 }
522
523 static class SingleConcreteImplementor implements SingleImplementorInterface {
524 }
525
526 interface SingleAbstractImplementorInterface {
527 }
528
529 abstract static class SingleAbstractImplementor implements SingleAbstractImplementorInterface {
530 }
531
532 interface MultiImplementorInterface {
533 }
534
535 static class ConcreteImplementor1 implements MultiImplementorInterface {
536 }
537
538 static class ConcreteImplementor2 implements MultiImplementorInterface {
539 }
540
541 interface MultipleAbstractImplementorInterface {
542 }
543
544 abstract static class MultiAbstractImplementor1 implements MultipleAbstractImplementorInterface {
545 }
546
547 abstract static class MultiAbstractImplementor2 implements MultipleAbstractImplementorInterface {
548 }
549
550 interface SingleAbstractImplementorInterface2 {
551 }
552
553 interface ExtendedSingleImplementorInterface {
554 }
555
556 abstract static class SingleAbstractImplementor2 implements SingleAbstractImplementorInterface2 {
557 }
558
559 static class ConcreteTransitiveImplementor1 extends SingleAbstractImplementor2 implements ExtendedSingleImplementorInterface {
560 }
561
562 static class ConcreteTransitiveImplementor2 extends SingleAbstractImplementor2 implements ExtendedSingleImplementorInterface {
563 }
564
565 @Test
566 public void getSingleImplementorTest() {
567 ResolvedJavaType iNi = metaAccess.lookupJavaType(NoImplementor.class);
568 assertNull(iNi.getSingleImplementor());
569
570 ResolvedJavaType iSi = metaAccess.lookupJavaType(SingleImplementorInterface.class);
571 ResolvedJavaType cSi = metaAccess.lookupJavaType(SingleConcreteImplementor.class);
572 assertEquals(cSi, iSi.getSingleImplementor());
573
574 ResolvedJavaType iSai = metaAccess.lookupJavaType(SingleAbstractImplementorInterface.class);
575 ResolvedJavaType aSai = metaAccess.lookupJavaType(SingleAbstractImplementor.class);
576 assertEquals(aSai, iSai.getSingleImplementor());
577
578 ResolvedJavaType iMi = metaAccess.lookupJavaType(MultiImplementorInterface.class);
579 metaAccess.lookupJavaType(ConcreteImplementor1.class);
580 metaAccess.lookupJavaType(ConcreteImplementor2.class);
581 assertEquals(iMi, iMi.getSingleImplementor());
582
583 ResolvedJavaType iMai = metaAccess.lookupJavaType(MultipleAbstractImplementorInterface.class);
584 metaAccess.lookupJavaType(MultiAbstractImplementor1.class);
585 metaAccess.lookupJavaType(MultiAbstractImplementor2.class);
586 assertEquals(iMai, iMai.getSingleImplementor());
587
588 ResolvedJavaType iSai2 = metaAccess.lookupJavaType(SingleAbstractImplementorInterface2.class);
589 ResolvedJavaType aSai2 = metaAccess.lookupJavaType(SingleAbstractImplementor2.class);
590 metaAccess.lookupJavaType(ConcreteTransitiveImplementor1.class);
591 metaAccess.lookupJavaType(ConcreteTransitiveImplementor2.class);
592 assertEquals(aSai2, iSai2.getSingleImplementor());
593
594 for (Class<?> c : classes) {
595 ResolvedJavaType type = metaAccess.lookupJavaType(c);
596 try {
597 type.getSingleImplementor();
598 if (!c.isInterface()) {
599 throw new AssertionError("Expected exception for calling getSingleImplmentor on " + c.getName());
600 }
601 } catch (JVMCIError e) {
602 if (c.isInterface()) {
603 throw new AssertionError("Unexpected exception", e);
604 }
605 }
606 }
607 }
608
609 @Test(expected = JVMCIError.class)
610 public void getSingleImplementorTestClassReceiver() {
611 ResolvedJavaType base = metaAccess.lookupJavaType(Base.class);
612 base.getSingleImplementor();
613 }
614
615 @Test(expected = JVMCIError.class)
616 public void getSingleImplementorTestPrimitiveReceiver() {
617 ResolvedJavaType primitive = metaAccess.lookupJavaType(int.class);
618 primitive.getSingleImplementor();
619 }
620
621 @Test
622 public void getComponentTypeTest() {
623 for (Class<?> c : classes) {
624 ResolvedJavaType type = metaAccess.lookupJavaType(c);
625 Class<?> expected = c.getComponentType();
626 ResolvedJavaType actual = type.getComponentType();
627 if (expected == null) {
628 assertNull(actual);
629 } else {
630 assertTrue(actual.equals(metaAccess.lookupJavaType(expected)));
631 }
632 }
633 }
634
635 @Test
636 public void getArrayClassTest() {
637 for (Class<?> c : classes) {
638 if (c != void.class) {
639 ResolvedJavaType type = metaAccess.lookupJavaType(c);
640 Class<?> expected = getArrayClass(c);
641 ResolvedJavaType actual = type.getArrayClass();
642 assertTrue(actual.equals(metaAccess.lookupJavaType(expected)));
643 }
644 }
645 }
646
647 static class Declarations {
648
649 final Method implementation;
650 final Set<Method> declarations;
651
652 Declarations(Method impl) {
653 this.implementation = impl;
654 declarations = new HashSet<>();
655 }
656 }
657
658 /**
659 * See <a href="http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.4.5">Method
660 * overriding</a>.
661 */
662 static boolean isOverriderOf(Method impl, Method m) {
663 if (!isPrivate(m.getModifiers()) && !isFinal(m.getModifiers())) {
664 if (m.getName().equals(impl.getName())) {
665 if (m.getReturnType() == impl.getReturnType()) {
666 if (Arrays.equals(m.getParameterTypes(), impl.getParameterTypes())) {
667 if (isPublic(m.getModifiers()) || isProtected(m.getModifiers())) {
668 // m is public or protected
669 return isPublic(impl.getModifiers()) || isProtected(impl.getModifiers());
670 } else {
671 // m is package-private
672 return impl.getDeclaringClass().getPackage() == m.getDeclaringClass().getPackage();
673 }
674 }
675 }
676 }
677 }
678 return false;
679 }
680
681 static final Map<Class<?>, VTable> vtables = new HashMap<>();
682
683 static class VTable {
684
685 final Map<NameAndSignature, Method> methods = new HashMap<>();
686 }
687
688 static synchronized VTable getVTable(Class<?> c) {
689 VTable vtable = vtables.get(c);
690 if (vtable == null) {
691 vtable = new VTable();
692 if (c != Object.class) {
693 VTable superVtable = getVTable(c.getSuperclass());
694 vtable.methods.putAll(superVtable.methods);
695 }
696 for (Method m : c.getDeclaredMethods()) {
697 if (!isStatic(m.getModifiers()) && !isPrivate(m.getModifiers())) {
698 if (isAbstract(m.getModifiers())) {
699 // A subclass makes a concrete method in a superclass abstract
700 vtable.methods.remove(new NameAndSignature(m));
701 } else {
702 vtable.methods.put(new NameAndSignature(m), m);
703 }
704 }
705 }
706 vtables.put(c, vtable);
707 }
708 return vtable;
709 }
710
711 static Set<Method> findDeclarations(Method impl, Class<?> c) {
712 Set<Method> declarations = new HashSet<>();
713 NameAndSignature implSig = new NameAndSignature(impl);
714 if (c != null) {
715 for (Method m : c.getDeclaredMethods()) {
716 if (new NameAndSignature(m).equals(implSig)) {
717 declarations.add(m);
718 break;
719 }
720 }
721 if (!c.isInterface()) {
722 declarations.addAll(findDeclarations(impl, c.getSuperclass()));
723 }
724 for (Class<?> i : c.getInterfaces()) {
725 declarations.addAll(findDeclarations(impl, i));
726 }
727 }
728 return declarations;
729 }
730
731 @Test
732 public void resolveMethodTest() {
733 ResolvedJavaType context = metaAccess.lookupJavaType(TestResolvedJavaType.class);
734 for (Class<?> c : classes) {
735 ResolvedJavaType type = metaAccess.lookupJavaType(c);
736 if (c.isInterface()) {
737 for (Method m : c.getDeclaredMethods()) {
738 ResolvedJavaMethod resolved = metaAccess.lookupJavaMethod(m);
739 ResolvedJavaMethod impl = type.resolveMethod(resolved, context);
740 assertEquals(m.toString(), null, impl);
741 }
742 } else if (c.isPrimitive()) {
743 assertEquals("No methods expected", c.getDeclaredMethods().length, 0);
744 } else {
745 VTable vtable = getVTable(c);
746 for (Method impl : vtable.methods.values()) {
747 Set<Method> decls = findDeclarations(impl, c);
748 for (Method decl : decls) {
749 ResolvedJavaMethod m = metaAccess.lookupJavaMethod(decl);
750 if (m.isPublic()) {
751 ResolvedJavaMethod resolvedMethod = type.resolveMethod(m, context);
752 if (isSignaturePolymorphic(m)) {
753 // Signature polymorphic methods must not be resolved
754 assertNull(resolvedMethod);
755 } else {
756 ResolvedJavaMethod i = metaAccess.lookupJavaMethod(impl);
757 assertEquals(m.toString(), i, resolvedMethod);
758 }
759 }
760 }
761 }
762 // For backwards compatibility treat constructors as resolvable even though they
763 // aren't virtually dispatched.
764 ResolvedJavaType declaringClass = metaAccess.lookupJavaType(c);
765 for (Constructor<?> m : c.getDeclaredConstructors()) {
766 ResolvedJavaMethod decl = metaAccess.lookupJavaMethod(m);
767 ResolvedJavaMethod impl = type.resolveMethod(decl, declaringClass);
768 assertEquals(m.toString(), decl, impl);
769 }
770 for (Method m : c.getDeclaredMethods()) {
771 if (isStatic(m.getModifiers())) {
772 // resolveMethod really shouldn't be called with static methods and the
773 // result is is somewhat inconsistent so just ignore them
774 continue;
775 }
776 ResolvedJavaMethod decl = metaAccess.lookupJavaMethod(m);
777 ResolvedJavaMethod impl = type.resolveMethod(decl, declaringClass);
778 ResolvedJavaMethod expected = isSignaturePolymorphic(decl) ? null : decl;
779 assertEquals(m.toString(), expected, impl);
780 }
781 }
782 }
783 }
784
785 @Test
786 public void resolveConcreteMethodTest() {
787 ResolvedJavaType context = metaAccess.lookupJavaType(TestResolvedJavaType.class);
788 for (Class<?> c : classes) {
789 ResolvedJavaType type = metaAccess.lookupJavaType(c);
790 if (c.isInterface()) {
791 for (Method m : c.getDeclaredMethods()) {
792 ResolvedJavaMethod resolved = metaAccess.lookupJavaMethod(m);
793 ResolvedJavaMethod impl = type.resolveConcreteMethod(resolved, context);
794 assertEquals(m.toString(), null, impl);
795 }
796 } else if (c.isPrimitive()) {
797 assertEquals("No methods expected", c.getDeclaredMethods().length, 0);
798 } else {
799 VTable vtable = getVTable(c);
800 for (Method impl : vtable.methods.values()) {
801 Set<Method> decls = findDeclarations(impl, c);
802 for (Method decl : decls) {
803 ResolvedJavaMethod m = metaAccess.lookupJavaMethod(decl);
804 if (m.isPublic()) {
805 ResolvedJavaMethod resolvedMethod = type.resolveConcreteMethod(m, context);
806 if (isSignaturePolymorphic(m)) {
807 // Signature polymorphic methods must not be resolved
808 assertNull(String.format("Got: %s", resolvedMethod), resolvedMethod);
809 } else {
810 ResolvedJavaMethod i = metaAccess.lookupJavaMethod(impl);
811 assertEquals(i, resolvedMethod);
812 }
813 }
814 }
815 }
816 for (Method m : c.getDeclaredMethods()) {
817 ResolvedJavaMethod impl = type.resolveConcreteMethod(metaAccess.lookupJavaMethod(m), context);
818 ResolvedJavaMethod expected = isAbstract(m.getModifiers()) ? null : impl;
819 assertEquals(type + " " + m.toString(), expected, impl);
820 }
821 }
822 }
823 }
824
825 @Test
826 public void findUniqueConcreteMethodTest() throws NoSuchMethodException {
827 ResolvedJavaMethod thisMethod = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("findUniqueConcreteMethodTest"));
828 ResolvedJavaMethod ucm = metaAccess.lookupJavaType(getClass()).findUniqueConcreteMethod(thisMethod).getResult();
829 assertEquals(thisMethod, ucm);
830 }
831
832 public static Set<Field> getInstanceFields(Class<?> c, boolean includeSuperclasses) {
833 if (c.isArray() || c.isPrimitive() || c.isInterface()) {
834 return Collections.emptySet();
835 }
836 Set<Field> result = new HashSet<>();
837 for (Field f : c.getDeclaredFields()) {
838 if (!Modifier.isStatic(f.getModifiers())) {
839 result.add(f);
840 }
841 }
842 if (includeSuperclasses && c != Object.class) {
843 result.addAll(getInstanceFields(c.getSuperclass(), true));
844 }
845 return result;
846 }
847
848 public static Set<Field> getStaticFields(Class<?> c) {
849 Set<Field> result = new HashSet<>();
850 for (Field f : c.getDeclaredFields()) {
851 if (Modifier.isStatic(f.getModifiers())) {
852 result.add(f);
853 }
854 }
855 return result;
856 }
857
858 public boolean fieldsEqual(Field f, ResolvedJavaField rjf) {
859 return rjf.getDeclaringClass().equals(metaAccess.lookupJavaType(f.getDeclaringClass())) && rjf.getName().equals(f.getName()) &&
860 rjf.getType().resolve(rjf.getDeclaringClass()).equals(metaAccess.lookupJavaType(f.getType()));
861 }
862
863 public ResolvedJavaField lookupField(ResolvedJavaField[] fields, Field key) {
864 for (ResolvedJavaField rf : fields) {
865 if (fieldsEqual(key, rf)) {
866 return rf;
867 }
868 }
869 return null;
870 }
871
872 public Field lookupField(Set<Field> fields, ResolvedJavaField key) {
873 for (Field f : fields) {
874 if (fieldsEqual(f, key)) {
875 return f;
876 }
877 }
878 return null;
879 }
880
881 private static boolean isHiddenFromReflection(ResolvedJavaField f) {
882 if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(Throwable.class)) && f.getName().equals("backtrace")) {
883 return true;
884 }
885 if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(ConstantPool.class)) && f.getName().equals("constantPoolOop")) {
886 return true;
887 }
888 if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(Class.class))) {
889 return f.getName().equals("classLoader") || f.getName().equals("classData");
890 }
891 if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(Lookup.class))) {
892 return f.getName().equals("allowedModes") || f.getName().equals("lookupClass");
893 }
894 if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(ClassLoader.class)) ||
895 f.getDeclaringClass().equals(metaAccess.lookupJavaType(AccessibleObject.class)) ||
896 f.getDeclaringClass().equals(metaAccess.lookupJavaType(Constructor.class)) ||
897 f.getDeclaringClass().equals(metaAccess.lookupJavaType(Field.class)) ||
898 f.getDeclaringClass().equals(metaAccess.lookupJavaType(Method.class)) ||
899 f.getDeclaringClass().equals(metaAccess.lookupJavaType(Module.class))) {
900 return true;
901 }
902 return false;
903 }
904
905 @Test
906 public void getInstanceFieldsTest() {
907 for (Class<?> c : classes) {
908 ResolvedJavaType type = metaAccess.lookupJavaType(c);
909 for (boolean includeSuperclasses : new boolean[]{true, false}) {
910 Set<Field> expected = getInstanceFields(c, includeSuperclasses);
911 ResolvedJavaField[] actual = type.getInstanceFields(includeSuperclasses);
912 for (Field f : expected) {
913 assertNotNull(lookupField(actual, f));
914 }
915 for (ResolvedJavaField rf : actual) {
916 if (!isHiddenFromReflection(rf)) {
917 assertEquals(rf.toString(), lookupField(expected, rf) != null, !rf.isInternal());
918 }
919 }
920
921 // Test stability of getInstanceFields
922 ResolvedJavaField[] actual2 = type.getInstanceFields(includeSuperclasses);
923 assertArrayEquals(actual, actual2);
924 }
925 }
926 }
927
928 @Test
929 public void getStaticFieldsTest() {
930 for (Class<?> c : classes) {
931 ResolvedJavaType type = metaAccess.lookupJavaType(c);
932 Set<Field> expected = getStaticFields(c);
933 ResolvedJavaField[] actual = type.getStaticFields();
934 for (Field f : expected) {
935 assertNotNull(lookupField(actual, f));
936 }
937 for (ResolvedJavaField rf : actual) {
938 if (!isHiddenFromReflection(rf)) {
939 assertEquals(lookupField(expected, rf) != null, !rf.isInternal());
940 }
941 }
942
943 // Test stability of getStaticFields
944 ResolvedJavaField[] actual2 = type.getStaticFields();
945 assertArrayEquals(actual, actual2);
946 }
947 }
948
949 @Test
950 public void getDeclaredMethodsTest() {
951 for (Class<?> c : classes) {
952 ResolvedJavaType type = metaAccess.lookupJavaType(c);
953 Method[] raw = c.getDeclaredMethods();
954 Set<ResolvedJavaMethod> expected = new HashSet<>();
955 for (Method m : raw) {
956 ResolvedJavaMethod resolvedMethod = metaAccess.lookupJavaMethod(m);
957 assertNotNull(resolvedMethod);
958 expected.add(resolvedMethod);
959 }
960 Set<ResolvedJavaMethod> actual = new HashSet<>(Arrays.asList(type.getDeclaredMethods()));
961 assertEquals(expected, actual);
962 }
963 }
964
965 static class A {
966 static String name = "foo";
967 }
968
969 static class B extends A {
970 }
971
972 static class C {
973 }
974
975 static class D {
976 void foo() {
977 // use of assertions causes the class to have a <clinit>
978 assert getClass() != null;
979 }
980 }
981
982 static class SubD extends D {
983
984 }
985
986 private static ResolvedJavaMethod getClassInitializer(Class<?> c) {
987 ResolvedJavaMethod clinit = metaAccess.lookupJavaType(c).getClassInitializer();
988 if (clinit != null) {
989 assertEquals(0, clinit.getAnnotations().length);
990 assertEquals(0, clinit.getDeclaredAnnotations().length);
991 }
992 return clinit;
993 }
994
995 @Test
996 public void getClassInitializerTest() {
997 assertNotNull(getClassInitializer(A.class));
998 assertNotNull(getClassInitializer(D.class));
999 assertNull(getClassInitializer(B.class));
1000 assertNull(getClassInitializer(C.class));
1001 assertNull(getClassInitializer(int.class));
1002 assertNull(getClassInitializer(void.class));
1003 for (Class<?> c : classes) {
1004 getClassInitializer(c);
1005 }
1006 }
1007
1008 @Test
1009 public void getAnnotationsTest() {
1010 for (Class<?> c : classes) {
1011 ResolvedJavaType type = metaAccess.lookupJavaType(c);
1012 assertArrayEquals(c.getAnnotations(), type.getAnnotations());
1013 }
1014 }
1015
1016 @Test
1017 public void getAnnotationTest() {
1018 for (Class<?> c : classes) {
1019 ResolvedJavaType type = metaAccess.lookupJavaType(c);
1020 for (Annotation a : c.getAnnotations()) {
1021 assertEquals(a, type.getAnnotation(a.annotationType()));
1022 }
1023 }
1024 }
1025
1026 @Test
1027 public void getSourceFileNameTest() {
1028 Class<?> c = Object.class;
1029 ResolvedJavaType type = metaAccess.lookupJavaType(c);
1030 assertEquals(type.getSourceFileName(), "Object.java");
1031 }
1032
1033 @Test
1034 public void memberClassesTest() {
1035 for (Class<?> c : classes) {
1036 ResolvedJavaType type = metaAccess.lookupJavaType(c);
1037 assertEquals(c.isLocalClass(), type.isLocal());
1038 assertEquals(c.isMemberClass(), type.isMember());
1039 Class<?> enclc = c.getEnclosingClass();
1040 ResolvedJavaType enclt = type.getEnclosingType();
1041 assertFalse(enclc == null ^ enclt == null);
1042 if (enclc != null) {
1043 assertEquals(enclt, metaAccess.lookupJavaType(enclc));
1044 }
1045 }
1046 }
1047
1048 @Test
1049 public void isLeafTest() {
1050 for (Class<?> c : classes) {
1051 ResolvedJavaType type = metaAccess.lookupJavaType(c);
1052 ResolvedJavaType arrayType = c != void.class ? metaAccess.lookupJavaType(getArrayClass(c)) : null;
1053 if (c.isPrimitive()) {
1054 assertTrue(type.isLeaf());
1055 assertTrue(arrayType == null || arrayType.isLeaf());
1056 } else {
1057 assertTrue(c.toString(), type.isLeaf() == arrayType.isLeaf());
1058 if (!c.isArray()) {
1059 assertTrue(c.toString(), type.isLeaf() == Modifier.isFinal(c.getModifiers()));
1060 }
1061 }
1062 }
1063 }
1064
1065 static class TrivialCloneable implements Cloneable {
1066 @Override
1067 protected Object clone() {
1068 return new TrivialCloneable();
1069 }
1070 }
1071
1072 @Test
1073 public void isCloneableWithAllocationTest() {
1074 ResolvedJavaType cloneable = metaAccess.lookupJavaType(Cloneable.class);
1075 for (Class<?> c : classes) {
1076 ResolvedJavaType type = metaAccess.lookupJavaType(c);
1077 if (type.isCloneableWithAllocation()) {
1078 // Only Cloneable types should be allocation cloneable
1079 assertTrue(c.toString(), cloneable.isAssignableFrom(type));
1080 }
1081 }
1082 /*
1083 * We can't know for sure which types should be allocation cloneable on a particular
1084 * platform but assume that at least totally trivial objects should be.
1085 */
1086 ResolvedJavaType trivialCloneable = metaAccess.lookupJavaType(TrivialCloneable.class);
1087 assertTrue(trivialCloneable.toString(), trivialCloneable.isCloneableWithAllocation());
1088 }
1089
1090 @Test
1091 public void findMethodTest() {
1092 try {
1093 ResolvedJavaMethod findFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("()V"));
1094 ResolvedJavaMethod expectedFoo = metaAccess.lookupJavaMethod(D.class.getDeclaredMethod("foo"));
1095 assertEquals(expectedFoo, findFoo);
1096
1097 ResolvedJavaMethod wrongReturnTypeFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("()I"));
1098 assertNull(wrongReturnTypeFoo);
1099
1100 ResolvedJavaMethod wrongArgumentsFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("(I)V"));
1101 assertNull(wrongArgumentsFoo);
1102
1103 ResolvedJavaMethod wrongNameFoo = metaAccess.lookupJavaType(D.class).findMethod("bar", metaAccess.parseMethodDescriptor("()V"));
1104 assertNull(wrongNameFoo);
1105
1106 ResolvedJavaMethod wrongClassFoo = metaAccess.lookupJavaType(SubD.class).findMethod("foo", metaAccess.parseMethodDescriptor("()V"));
1107 assertNull(wrongClassFoo);
1108 } catch (NoSuchMethodException | SecurityException e) {
1109 throw new RuntimeException(e);
1110 }
1111 }
1112
1113 private Method findTestMethod(Method apiMethod) {
1114 String testName = apiMethod.getName() + "Test";
1115 for (Method m : getClass().getDeclaredMethods()) {
1116 if (m.getName().equals(testName) && m.getAnnotation(Test.class) != null) {
1117 return m;
1118 }
1119 }
1120 return null;
1121 }
1122
1123 // @formatter:off
1124 private static final String[] untestedApiMethods = {
1125 "initialize",
1126 "isPrimitive",
1127 "newArray",
1128 "getDeclaredConstructors",
1129 "isInitialized",
1130 "isLinked",
1131 "getJavaClass",
1132 "getObjectHub",
1133 "getHostClass",
1134 "hasFinalizableSubclass",
1135 "hasFinalizer",
1136 "isLocal",
1137 "isJavaLangObject",
1138 "isMember",
1139 "getElementalType",
1140 "getEnclosingType",
1141 "lookupType",
1142 "resolveField",
1143 "$jacocoInit"
1144 };
1145 // @formatter:on
1146
1147 /**
1148 * Ensures that any new methods added to {@link ResolvedJavaMethod} either have a test written
1149 * for them or are added to {@link #untestedApiMethods}.
1150 */
1151 @Test
1152 public void testCoverage() {
1153 Set<String> known = new HashSet<>(Arrays.asList(untestedApiMethods));
1154 for (Method m : ResolvedJavaType.class.getDeclaredMethods()) {
1155 if (findTestMethod(m) == null) {
1156 assertTrue("test missing for " + m, known.contains(m.getName()));
1157 } else {
1158 assertFalse("test should be removed from untestedApiMethods" + m, known.contains(m.getName()));
1159 }
1160 }
1161 }
1162
1163 private static boolean isSignaturePolymorphic(ResolvedJavaMethod method) {
1164 return method.getAnnotation(SIGNATURE_POLYMORPHIC_CLASS) != null;
1165 }
1166 }
--- EOF ---