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