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 private static Class<?> asExactClass(Class<?> c) { 187 if (c.isArray()) { 188 if (asExactClass(c.getComponentType()) != null) { 189 return c; 190 } 191 } else { 192 if (c.isPrimitive() || Modifier.isFinal(c.getModifiers())) { 193 return c; 194 } 195 } 196 return null; 197 } 198 199 @Test 200 public void asExactTypeTest() { 201 for (Class<?> c : classes) { 202 ResolvedJavaType type = metaAccess.lookupJavaType(c); 203 ResolvedJavaType exactType = type.asExactType(); 204 Class<?> expected = asExactClass(c); 205 if (expected == null) { 206 assertTrue("exact(" + c.getName() + ") != null", exactType == null); 207 } else { 208 assertNotNull(exactType); 209 assertTrue(exactType.equals(metaAccess.lookupJavaType(expected))); 210 } 211 } 212 } 213 214 @Test 215 public void getSuperclassTest() { 216 for (Class<?> c : classes) { 217 ResolvedJavaType type = metaAccess.lookupJavaType(c); 218 Class<?> expected = c.getSuperclass(); 219 ResolvedJavaType actual = type.getSuperclass(); 220 if (expected == null) { 221 assertTrue(actual == null); 222 } else { 223 assertNotNull(actual); 224 assertTrue(actual.equals(metaAccess.lookupJavaType(expected))); 225 } 226 } 227 } 228 229 @Test 230 public void getInterfacesTest() { 231 for (Class<?> c : classes) { 232 ResolvedJavaType type = metaAccess.lookupJavaType(c); 233 Class<?>[] expected = c.getInterfaces(); 234 ResolvedJavaType[] actual = type.getInterfaces(); 235 assertEquals(expected.length, actual.length); 236 for (int i = 0; i < expected.length; i++) { 237 assertTrue(actual[i].equals(metaAccess.lookupJavaType(expected[i]))); 238 } 239 } 240 } 241 242 public Class<?> getSupertype(Class<?> c) { 243 assert !c.isPrimitive(); 244 if (c.isArray()) { 245 Class<?> componentType = c.getComponentType(); 246 if (componentType.isPrimitive() || componentType == Object.class) { 247 return Object.class; 248 } 249 return getArrayClass(getSupertype(componentType)); 250 } 251 if (c.isInterface()) { 252 return Object.class; 253 } 254 return c.getSuperclass(); 255 } 256 257 public Class<?> findLeastCommonAncestor(Class<?> c1Initial, Class<?> c2Initial) { 258 if (c1Initial.isPrimitive() || c2Initial.isPrimitive()) { 259 return null; 260 } else { 261 Class<?> c1 = c1Initial; 262 Class<?> c2 = c2Initial; 263 while (true) { 264 if (c1.isAssignableFrom(c2)) { 265 return c1; 266 } 267 if (c2.isAssignableFrom(c1)) { 268 return c2; 269 } 270 c1 = getSupertype(c1); 271 c2 = getSupertype(c2); 272 } 273 } 274 } 275 276 @Test 277 public void findLeastCommonAncestorTest() { 278 Class<?>[] all = classes.toArray(new Class<?>[classes.size()]); 279 for (int i = 0; i < all.length; i++) { 280 Class<?> c1 = all[i]; 281 for (int j = i; j < all.length; j++) { 282 Class<?> c2 = all[j]; 283 ResolvedJavaType t1 = metaAccess.lookupJavaType(c1); 284 ResolvedJavaType t2 = metaAccess.lookupJavaType(c2); 285 Class<?> expected = findLeastCommonAncestor(c1, c2); 286 ResolvedJavaType actual = t1.findLeastCommonAncestor(t2); 287 if (expected == null) { 288 assertTrue(actual == null); 289 } else { 290 assertNotNull(actual); 291 assertTrue(actual.equals(metaAccess.lookupJavaType(expected))); 292 } 293 } 294 } 295 } 296 297 private static class Base { 298 } 299 300 abstract static class Abstract1 extends Base { 301 } 302 303 interface Interface1 { 304 } 305 306 static class Concrete1 extends Abstract1 { 307 } 308 309 static class Concrete2 extends Abstract1 implements Interface1 { 310 } 311 312 static class Concrete3 extends Concrete2 { 313 } 314 315 static final class Final1 extends Abstract1 { 316 } 317 318 abstract static class Abstract4 extends Concrete3 { 319 } 320 321 void checkConcreteSubtype(ResolvedJavaType type, ResolvedJavaType expected) { 322 AssumptionResult<ResolvedJavaType> leafConcreteSubtype = type.findLeafConcreteSubtype(); 323 if (leafConcreteSubtype == null) { 324 // findLeafConcreteSubtype() is conservative 325 } else { 326 if (expected == null) { 327 assertNull(leafConcreteSubtype); 328 } else { 329 assertTrue(leafConcreteSubtype.getResult().equals(expected)); 330 } 331 assertTrue(!type.isLeaf() || leafConcreteSubtype.isAssumptionFree()); 332 } 333 334 if (!type.isArray()) { 335 ResolvedJavaType arrayType = type.getArrayClass(); 336 AssumptionResult<ResolvedJavaType> arraySubtype = arrayType.findLeafConcreteSubtype(); 337 if (arraySubtype != null) { 338 assertEquals(arraySubtype.getResult(), arrayType); 339 } else { 340 // findLeafConcreteSubtype() method is conservative 341 } 342 } 343 } 344 345 @Test 346 public void findLeafConcreteSubtypeTest() { 347 ResolvedJavaType base = metaAccess.lookupJavaType(Base.class); 348 checkConcreteSubtype(base, base); 349 350 ResolvedJavaType a1 = metaAccess.lookupJavaType(Abstract1.class); 351 ResolvedJavaType c1 = metaAccess.lookupJavaType(Concrete1.class); 352 353 checkConcreteSubtype(base, null); 354 checkConcreteSubtype(a1, c1); 355 checkConcreteSubtype(c1, c1); 356 357 ResolvedJavaType i1 = metaAccess.lookupJavaType(Interface1.class); 358 ResolvedJavaType c2 = metaAccess.lookupJavaType(Concrete2.class); 359 360 checkConcreteSubtype(base, null); 361 checkConcreteSubtype(a1, null); 362 checkConcreteSubtype(c1, c1); 363 checkConcreteSubtype(i1, c2); 364 checkConcreteSubtype(c2, c2); 365 366 ResolvedJavaType c3 = metaAccess.lookupJavaType(Concrete3.class); 367 checkConcreteSubtype(c2, null); 368 checkConcreteSubtype(c3, c3); 369 370 ResolvedJavaType a4 = metaAccess.lookupJavaType(Abstract4.class); 371 checkConcreteSubtype(c3, null); 372 checkConcreteSubtype(a4, null); 373 374 ResolvedJavaType a1a = metaAccess.lookupJavaType(Abstract1[].class); 375 checkConcreteSubtype(a1a, null); 376 ResolvedJavaType i1a = metaAccess.lookupJavaType(Interface1[].class); 377 checkConcreteSubtype(i1a, null); 378 ResolvedJavaType c1a = metaAccess.lookupJavaType(Concrete1[].class); 379 checkConcreteSubtype(c1a, c1a); 380 ResolvedJavaType f1a = metaAccess.lookupJavaType(Final1[].class); 381 checkConcreteSubtype(f1a, f1a); 382 383 ResolvedJavaType obja = metaAccess.lookupJavaType(Object[].class); 384 checkConcreteSubtype(obja, null); 385 386 ResolvedJavaType inta = metaAccess.lookupJavaType(int[].class); 387 checkConcreteSubtype(inta, inta); 388 } 389 390 interface NoImplementor { 391 } 392 393 interface SingleImplementorInterface { 394 } 395 396 static class SingleConcreteImplementor implements SingleImplementorInterface { 397 } 398 399 interface SingleAbstractImplementorInterface { 400 } 401 402 abstract static class SingleAbstractImplementor implements SingleAbstractImplementorInterface { 403 } 404 405 interface MultiImplementorInterface { 406 } 407 408 static class ConcreteImplementor1 implements MultiImplementorInterface { 409 } 410 411 static class ConcreteImplementor2 implements MultiImplementorInterface { 412 } 413 414 interface MultipleAbstractImplementorInterface { 415 } 416 417 abstract static class MultiAbstractImplementor1 implements MultipleAbstractImplementorInterface { 418 } 419 420 abstract static class MultiAbstractImplementor2 implements MultipleAbstractImplementorInterface { 421 } 422 423 interface SingleAbstractImplementorInterface2 { 424 } 425 426 interface ExtendedSingleImplementorInterface { 427 } 428 429 abstract static class SingleAbstractImplementor2 implements SingleAbstractImplementorInterface2 { 430 } 431 432 static class ConcreteTransitiveImplementor1 extends SingleAbstractImplementor2 implements ExtendedSingleImplementorInterface { 433 } 434 435 static class ConcreteTransitiveImplementor2 extends SingleAbstractImplementor2 implements ExtendedSingleImplementorInterface { 436 } 437 438 @Test 439 public void getSingleImplementorTest() { 440 ResolvedJavaType iNi = metaAccess.lookupJavaType(NoImplementor.class); 441 assertNull(iNi.getSingleImplementor()); 442 443 ResolvedJavaType iSi = metaAccess.lookupJavaType(SingleImplementorInterface.class); 444 ResolvedJavaType cSi = metaAccess.lookupJavaType(SingleConcreteImplementor.class); 445 assertEquals(cSi, iSi.getSingleImplementor()); 446 447 ResolvedJavaType iSai = metaAccess.lookupJavaType(SingleAbstractImplementorInterface.class); 448 ResolvedJavaType aSai = metaAccess.lookupJavaType(SingleAbstractImplementor.class); 449 assertEquals(aSai, iSai.getSingleImplementor()); 450 451 ResolvedJavaType iMi = metaAccess.lookupJavaType(MultiImplementorInterface.class); 452 metaAccess.lookupJavaType(ConcreteImplementor1.class); 453 metaAccess.lookupJavaType(ConcreteImplementor2.class); 454 assertEquals(iMi, iMi.getSingleImplementor()); 455 456 ResolvedJavaType iMai = metaAccess.lookupJavaType(MultipleAbstractImplementorInterface.class); 457 metaAccess.lookupJavaType(MultiAbstractImplementor1.class); 458 metaAccess.lookupJavaType(MultiAbstractImplementor2.class); 459 assertEquals(iMai, iMai.getSingleImplementor()); 460 461 ResolvedJavaType iSai2 = metaAccess.lookupJavaType(SingleAbstractImplementorInterface2.class); 462 ResolvedJavaType aSai2 = metaAccess.lookupJavaType(SingleAbstractImplementor2.class); 463 metaAccess.lookupJavaType(ConcreteTransitiveImplementor1.class); 464 metaAccess.lookupJavaType(ConcreteTransitiveImplementor2.class); 465 assertEquals(aSai2, iSai2.getSingleImplementor()); 466 } 467 468 @Test(expected = JVMCIError.class) 469 public void getSingleImplementorTestClassReceiver() { 470 ResolvedJavaType base = metaAccess.lookupJavaType(Base.class); 471 base.getSingleImplementor(); 472 } 473 474 @Test(expected = JVMCIError.class) 475 public void getSingleImplementorTestPrimitiveReceiver() { 476 ResolvedJavaType primitive = metaAccess.lookupJavaType(int.class); 477 primitive.getSingleImplementor(); 478 } 479 480 @Test 481 public void getComponentTypeTest() { 482 for (Class<?> c : classes) { 483 ResolvedJavaType type = metaAccess.lookupJavaType(c); 484 Class<?> expected = c.getComponentType(); 485 ResolvedJavaType actual = type.getComponentType(); 486 if (expected == null) { 487 assertNull(actual); 488 } else { 489 assertTrue(actual.equals(metaAccess.lookupJavaType(expected))); 490 } 491 } 492 } 493 494 @Test 495 public void getArrayClassTest() { 496 for (Class<?> c : classes) { 497 if (c != void.class) { 498 ResolvedJavaType type = metaAccess.lookupJavaType(c); 499 Class<?> expected = getArrayClass(c); 500 ResolvedJavaType actual = type.getArrayClass(); 501 assertTrue(actual.equals(metaAccess.lookupJavaType(expected))); 502 } 503 } 504 } 505 506 static class Declarations { 507 508 final Method implementation; 509 final Set<Method> declarations; 510 511 Declarations(Method impl) { 512 this.implementation = impl; 513 declarations = new HashSet<>(); 514 } 515 } 516 517 /** 518 * See <a href="http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.4.5">Method 519 * overriding</a>. 520 */ 521 static boolean isOverriderOf(Method impl, Method m) { 522 if (!isPrivate(m.getModifiers()) && !isFinal(m.getModifiers())) { 523 if (m.getName().equals(impl.getName())) { 524 if (m.getReturnType() == impl.getReturnType()) { 525 if (Arrays.equals(m.getParameterTypes(), impl.getParameterTypes())) { 526 if (isPublic(m.getModifiers()) || isProtected(m.getModifiers())) { 527 // m is public or protected 528 return isPublic(impl.getModifiers()) || isProtected(impl.getModifiers()); 529 } else { 530 // m is package-private 531 return impl.getDeclaringClass().getPackage() == m.getDeclaringClass().getPackage(); 532 } 533 } 534 } 535 } 536 } 537 return false; 538 } 539 540 static final Map<Class<?>, VTable> vtables = new HashMap<>(); 541 542 static class VTable { 543 544 final Map<NameAndSignature, Method> methods = new HashMap<>(); 545 } 546 547 static synchronized VTable getVTable(Class<?> c) { 548 VTable vtable = vtables.get(c); 549 if (vtable == null) { 550 vtable = new VTable(); 551 if (c != Object.class) { 552 VTable superVtable = getVTable(c.getSuperclass()); 553 vtable.methods.putAll(superVtable.methods); 554 } 555 for (Method m : c.getDeclaredMethods()) { 556 if (!isStatic(m.getModifiers()) && !isPrivate(m.getModifiers())) { 557 if (isAbstract(m.getModifiers())) { 558 // A subclass makes a concrete method in a superclass abstract 559 vtable.methods.remove(new NameAndSignature(m)); 560 } else { 561 vtable.methods.put(new NameAndSignature(m), m); 562 } 563 } 564 } 565 vtables.put(c, vtable); 566 } 567 return vtable; 568 } 569 570 static Set<Method> findDeclarations(Method impl, Class<?> c) { 571 Set<Method> declarations = new HashSet<>(); 572 NameAndSignature implSig = new NameAndSignature(impl); 573 if (c != null) { 574 for (Method m : c.getDeclaredMethods()) { 575 if (new NameAndSignature(m).equals(implSig)) { 576 declarations.add(m); 577 break; 578 } 579 } 580 if (!c.isInterface()) { 581 declarations.addAll(findDeclarations(impl, c.getSuperclass())); 582 } 583 for (Class<?> i : c.getInterfaces()) { 584 declarations.addAll(findDeclarations(impl, i)); 585 } 586 } 587 return declarations; 588 } 589 590 @Test 591 public void resolveMethodTest() { 592 ResolvedJavaType context = metaAccess.lookupJavaType(TestResolvedJavaType.class); 593 for (Class<?> c : classes) { 594 ResolvedJavaType type = metaAccess.lookupJavaType(c); 595 if (c.isInterface()) { 596 for (Method m : c.getDeclaredMethods()) { 597 ResolvedJavaMethod resolved = metaAccess.lookupJavaMethod(m); 598 ResolvedJavaMethod impl = type.resolveMethod(resolved, context); 599 assertEquals(m.toString(), null, impl); 600 } 601 } else if (c.isPrimitive()) { 602 assertEquals("No methods expected", c.getDeclaredMethods().length, 0); 603 } else { 604 VTable vtable = getVTable(c); 605 for (Method impl : vtable.methods.values()) { 606 Set<Method> decls = findDeclarations(impl, c); 607 for (Method decl : decls) { 608 ResolvedJavaMethod m = metaAccess.lookupJavaMethod(decl); 609 if (m.isPublic()) { 610 ResolvedJavaMethod i = metaAccess.lookupJavaMethod(impl); 611 assertEquals(m.toString(), i, type.resolveMethod(m, context)); 612 } 613 } 614 } 615 } 616 } 617 } 618 619 @Test 620 public void resolveConcreteMethodTest() { 621 ResolvedJavaType context = metaAccess.lookupJavaType(TestResolvedJavaType.class); 622 for (Class<?> c : classes) { 623 ResolvedJavaType type = metaAccess.lookupJavaType(c); 624 if (c.isInterface()) { 625 for (Method m : c.getDeclaredMethods()) { 626 ResolvedJavaMethod resolved = metaAccess.lookupJavaMethod(m); 627 ResolvedJavaMethod impl = type.resolveConcreteMethod(resolved, context); 628 assertEquals(m.toString(), null, impl); 629 } 630 } else if (c.isPrimitive()) { 631 assertEquals("No methods expected", c.getDeclaredMethods().length, 0); 632 } else { 633 VTable vtable = getVTable(c); 634 for (Method impl : vtable.methods.values()) { 635 Set<Method> decls = findDeclarations(impl, c); 636 for (Method decl : decls) { 637 ResolvedJavaMethod m = metaAccess.lookupJavaMethod(decl); 638 if (m.isPublic()) { 639 ResolvedJavaMethod i = metaAccess.lookupJavaMethod(impl); 640 assertEquals(i, type.resolveConcreteMethod(m, context)); 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 }