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