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