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