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