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