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 } 332 333 if (!type.isArray()) { 334 ResolvedJavaType arrayType = type.getArrayClass(); 335 AssumptionResult<ResolvedJavaType> arraySubtype = arrayType.findLeafConcreteSubtype(); 336 if (arraySubtype != null) { 337 assertEquals(arraySubtype.getResult(), arrayType); 338 } else { 339 // findLeafConcreteSubtype() method is conservative 340 } 341 } 342 } 343 344 @Test 345 public void findLeafConcreteSubtypeTest() { 346 ResolvedJavaType base = metaAccess.lookupJavaType(Base.class); 347 checkConcreteSubtype(base, base); 348 349 ResolvedJavaType a1 = metaAccess.lookupJavaType(Abstract1.class); 350 ResolvedJavaType c1 = metaAccess.lookupJavaType(Concrete1.class); 351 352 checkConcreteSubtype(base, null); 353 checkConcreteSubtype(a1, c1); 354 checkConcreteSubtype(c1, c1); 355 356 ResolvedJavaType i1 = metaAccess.lookupJavaType(Interface1.class); 357 ResolvedJavaType c2 = metaAccess.lookupJavaType(Concrete2.class); 358 359 checkConcreteSubtype(base, null); 360 checkConcreteSubtype(a1, null); 361 checkConcreteSubtype(c1, c1); 362 checkConcreteSubtype(i1, c2); 363 checkConcreteSubtype(c2, c2); 364 365 ResolvedJavaType c3 = metaAccess.lookupJavaType(Concrete3.class); 366 checkConcreteSubtype(c2, null); 367 checkConcreteSubtype(c3, c3); 368 369 ResolvedJavaType a4 = metaAccess.lookupJavaType(Abstract4.class); 370 checkConcreteSubtype(c3, null); 371 checkConcreteSubtype(a4, null); 372 373 ResolvedJavaType a1a = metaAccess.lookupJavaType(Abstract1[].class); 374 checkConcreteSubtype(a1a, null); 375 ResolvedJavaType c1a = metaAccess.lookupJavaType(Concrete1[].class); 376 checkConcreteSubtype(c1a, null); 377 ResolvedJavaType f1a = metaAccess.lookupJavaType(Final1[].class); 378 checkConcreteSubtype(f1a, f1a); 379 380 ResolvedJavaType obja = metaAccess.lookupJavaType(Object[].class); 381 checkConcreteSubtype(obja, null); 382 383 ResolvedJavaType inta = metaAccess.lookupJavaType(int[].class); 384 checkConcreteSubtype(inta, inta); 385 } 386 387 interface NoImplementor { 388 } 389 390 interface SingleImplementorInterface { 391 } 392 393 static class SingleConcreteImplementor implements SingleImplementorInterface { 394 } 395 396 interface SingleAbstractImplementorInterface { 397 } 398 399 abstract static class SingleAbstractImplementor implements SingleAbstractImplementorInterface { 400 } 401 402 interface MultiImplementorInterface { 403 } 404 405 static class ConcreteImplementor1 implements MultiImplementorInterface { 406 } 407 408 static class ConcreteImplementor2 implements MultiImplementorInterface { 409 } 410 411 interface MultipleAbstractImplementorInterface { 412 } 413 414 abstract static class MultiAbstractImplementor1 implements MultipleAbstractImplementorInterface { 415 } 416 417 abstract static class MultiAbstractImplementor2 implements MultipleAbstractImplementorInterface { 418 } 419 420 interface SingleAbstractImplementorInterface2 { 421 } 422 423 interface ExtendedSingleImplementorInterface { 424 } 425 426 abstract static class SingleAbstractImplementor2 implements SingleAbstractImplementorInterface2 { 427 } 428 429 static class ConcreteTransitiveImplementor1 extends SingleAbstractImplementor2 implements ExtendedSingleImplementorInterface { 430 } 431 432 static class ConcreteTransitiveImplementor2 extends SingleAbstractImplementor2 implements ExtendedSingleImplementorInterface { 433 } 434 435 @Test 436 public void getSingleImplementorTest() { 437 ResolvedJavaType iNi = metaAccess.lookupJavaType(NoImplementor.class); 438 assertNull(iNi.getSingleImplementor()); 439 440 ResolvedJavaType iSi = metaAccess.lookupJavaType(SingleImplementorInterface.class); 441 ResolvedJavaType cSi = metaAccess.lookupJavaType(SingleConcreteImplementor.class); 442 assertEquals(cSi, iSi.getSingleImplementor()); 443 444 ResolvedJavaType iSai = metaAccess.lookupJavaType(SingleAbstractImplementorInterface.class); 445 ResolvedJavaType aSai = metaAccess.lookupJavaType(SingleAbstractImplementor.class); 446 assertEquals(aSai, iSai.getSingleImplementor()); 447 448 ResolvedJavaType iMi = metaAccess.lookupJavaType(MultiImplementorInterface.class); 449 metaAccess.lookupJavaType(ConcreteImplementor1.class); 450 metaAccess.lookupJavaType(ConcreteImplementor2.class); 451 assertEquals(iMi, iMi.getSingleImplementor()); 452 453 ResolvedJavaType iMai = metaAccess.lookupJavaType(MultipleAbstractImplementorInterface.class); 454 metaAccess.lookupJavaType(MultiAbstractImplementor1.class); 455 metaAccess.lookupJavaType(MultiAbstractImplementor2.class); 456 assertEquals(iMai, iMai.getSingleImplementor()); 457 458 ResolvedJavaType iSai2 = metaAccess.lookupJavaType(SingleAbstractImplementorInterface2.class); 459 ResolvedJavaType aSai2 = metaAccess.lookupJavaType(SingleAbstractImplementor2.class); 460 metaAccess.lookupJavaType(ConcreteTransitiveImplementor1.class); 461 metaAccess.lookupJavaType(ConcreteTransitiveImplementor2.class); 462 assertEquals(aSai2, iSai2.getSingleImplementor()); 463 } 464 465 @Test(expected = JVMCIError.class) 466 public void getSingleImplementorTestClassReceiver() { 467 ResolvedJavaType base = metaAccess.lookupJavaType(Base.class); 468 base.getSingleImplementor(); 469 } 470 471 @Test(expected = JVMCIError.class) 472 public void getSingleImplementorTestPrimitiveReceiver() { 473 ResolvedJavaType primitive = metaAccess.lookupJavaType(int.class); 474 primitive.getSingleImplementor(); 475 } 476 477 @Test 478 public void getComponentTypeTest() { 479 for (Class<?> c : classes) { 480 ResolvedJavaType type = metaAccess.lookupJavaType(c); 481 Class<?> expected = c.getComponentType(); 482 ResolvedJavaType actual = type.getComponentType(); 483 if (expected == null) { 484 assertNull(actual); 485 } else { 486 assertTrue(actual.equals(metaAccess.lookupJavaType(expected))); 487 } 488 } 489 } 490 491 @Test 492 public void getArrayClassTest() { 493 for (Class<?> c : classes) { 494 if (c != void.class) { 495 ResolvedJavaType type = metaAccess.lookupJavaType(c); 496 Class<?> expected = getArrayClass(c); 497 ResolvedJavaType actual = type.getArrayClass(); 498 assertTrue(actual.equals(metaAccess.lookupJavaType(expected))); 499 } 500 } 501 } 502 503 static class Declarations { 504 505 final Method implementation; 506 final Set<Method> declarations; 507 508 Declarations(Method impl) { 509 this.implementation = impl; 510 declarations = new HashSet<>(); 511 } 512 } 513 514 /** 515 * See <a href="http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.4.5">Method 516 * overriding</a>. 517 */ 518 static boolean isOverriderOf(Method impl, Method m) { 519 if (!isPrivate(m.getModifiers()) && !isFinal(m.getModifiers())) { 520 if (m.getName().equals(impl.getName())) { 521 if (m.getReturnType() == impl.getReturnType()) { 522 if (Arrays.equals(m.getParameterTypes(), impl.getParameterTypes())) { 523 if (isPublic(m.getModifiers()) || isProtected(m.getModifiers())) { 524 // m is public or protected 525 return isPublic(impl.getModifiers()) || isProtected(impl.getModifiers()); 526 } else { 527 // m is package-private 528 return impl.getDeclaringClass().getPackage() == m.getDeclaringClass().getPackage(); 529 } 530 } 531 } 532 } 533 } 534 return false; 535 } 536 537 static final Map<Class<?>, VTable> vtables = new HashMap<>(); 538 539 static class VTable { 540 541 final Map<NameAndSignature, Method> methods = new HashMap<>(); 542 } 543 544 static synchronized VTable getVTable(Class<?> c) { 545 VTable vtable = vtables.get(c); 546 if (vtable == null) { 547 vtable = new VTable(); 548 if (c != Object.class) { 549 VTable superVtable = getVTable(c.getSuperclass()); 550 vtable.methods.putAll(superVtable.methods); 551 } 552 for (Method m : c.getDeclaredMethods()) { 553 if (!isStatic(m.getModifiers()) && !isPrivate(m.getModifiers())) { 554 if (isAbstract(m.getModifiers())) { 555 // A subclass makes a concrete method in a superclass abstract 556 vtable.methods.remove(new NameAndSignature(m)); 557 } else { 558 vtable.methods.put(new NameAndSignature(m), m); 559 } 560 } 561 } 562 vtables.put(c, vtable); 563 } 564 return vtable; 565 } 566 567 static Set<Method> findDeclarations(Method impl, Class<?> c) { 568 Set<Method> declarations = new HashSet<>(); 569 NameAndSignature implSig = new NameAndSignature(impl); 570 if (c != null) { 571 for (Method m : c.getDeclaredMethods()) { 572 if (new NameAndSignature(m).equals(implSig)) { 573 declarations.add(m); 574 break; 575 } 576 } 577 if (!c.isInterface()) { 578 declarations.addAll(findDeclarations(impl, c.getSuperclass())); 579 } 580 for (Class<?> i : c.getInterfaces()) { 581 declarations.addAll(findDeclarations(impl, i)); 582 } 583 } 584 return declarations; 585 } 586 587 @Test 588 public void resolveMethodTest() { 589 ResolvedJavaType context = metaAccess.lookupJavaType(TestResolvedJavaType.class); 590 for (Class<?> c : classes) { 591 ResolvedJavaType type = metaAccess.lookupJavaType(c); 592 if (c.isInterface()) { 593 for (Method m : c.getDeclaredMethods()) { 594 ResolvedJavaMethod resolved = metaAccess.lookupJavaMethod(m); 595 ResolvedJavaMethod impl = type.resolveMethod(resolved, context); 596 assertEquals(m.toString(), null, impl); 597 } 598 } else if (c.isPrimitive()) { 599 assertEquals("No methods expected", c.getDeclaredMethods().length, 0); 600 } else { 601 VTable vtable = getVTable(c); 602 for (Method impl : vtable.methods.values()) { 603 Set<Method> decls = findDeclarations(impl, c); 604 for (Method decl : decls) { 605 ResolvedJavaMethod m = metaAccess.lookupJavaMethod(decl); 606 if (m.isPublic()) { 607 ResolvedJavaMethod i = metaAccess.lookupJavaMethod(impl); 608 assertEquals(m.toString(), i, type.resolveMethod(m, context)); 609 } 610 } 611 } 612 } 613 } 614 } 615 616 @Test 617 public void resolveConcreteMethodTest() { 618 ResolvedJavaType context = metaAccess.lookupJavaType(TestResolvedJavaType.class); 619 for (Class<?> c : classes) { 620 ResolvedJavaType type = metaAccess.lookupJavaType(c); 621 if (c.isInterface()) { 622 for (Method m : c.getDeclaredMethods()) { 623 ResolvedJavaMethod resolved = metaAccess.lookupJavaMethod(m); 624 ResolvedJavaMethod impl = type.resolveConcreteMethod(resolved, context); 625 assertEquals(m.toString(), null, impl); 626 } 627 } else if (c.isPrimitive()) { 628 assertEquals("No methods expected", c.getDeclaredMethods().length, 0); 629 } else { 630 VTable vtable = getVTable(c); 631 for (Method impl : vtable.methods.values()) { 632 Set<Method> decls = findDeclarations(impl, c); 633 for (Method decl : decls) { 634 ResolvedJavaMethod m = metaAccess.lookupJavaMethod(decl); 635 if (m.isPublic()) { 636 ResolvedJavaMethod i = metaAccess.lookupJavaMethod(impl); 637 assertEquals(i, type.resolveConcreteMethod(m, context)); 638 } 639 } 640 } 641 for (Method m : c.getDeclaredMethods()) { 642 ResolvedJavaMethod impl = type.resolveConcreteMethod(metaAccess.lookupJavaMethod(m), context); 643 ResolvedJavaMethod expected = isAbstract(m.getModifiers()) ? null : impl; 644 assertEquals(type + " " + m.toString(), expected, impl); 645 } 646 } 647 } 648 } 649 650 @Test 651 public void findUniqueConcreteMethodTest() throws NoSuchMethodException { 652 ResolvedJavaMethod thisMethod = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("findUniqueConcreteMethodTest")); 653 ResolvedJavaMethod ucm = metaAccess.lookupJavaType(getClass()).findUniqueConcreteMethod(thisMethod).getResult(); 654 assertEquals(thisMethod, ucm); 655 } 656 657 public static Set<Field> getInstanceFields(Class<?> c, boolean includeSuperclasses) { 658 if (c.isArray() || c.isPrimitive() || c.isInterface()) { 659 return Collections.emptySet(); 660 } 661 Set<Field> result = new HashSet<>(); 662 for (Field f : c.getDeclaredFields()) { 663 if (!Modifier.isStatic(f.getModifiers())) { 664 result.add(f); 665 } 666 } 667 if (includeSuperclasses && c != Object.class) { 668 result.addAll(getInstanceFields(c.getSuperclass(), true)); 669 } 670 return result; 671 } 672 673 public static Set<Field> getStaticFields(Class<?> c) { 674 Set<Field> result = new HashSet<>(); 675 for (Field f : c.getDeclaredFields()) { 676 if (Modifier.isStatic(f.getModifiers())) { 677 result.add(f); 678 } 679 } 680 return result; 681 } 682 683 public boolean fieldsEqual(Field f, ResolvedJavaField rjf) { 684 return rjf.getDeclaringClass().equals(metaAccess.lookupJavaType(f.getDeclaringClass())) && rjf.getName().equals(f.getName()) && 685 rjf.getType().resolve(rjf.getDeclaringClass()).equals(metaAccess.lookupJavaType(f.getType())); 686 } 687 688 public ResolvedJavaField lookupField(ResolvedJavaField[] fields, Field key) { 689 for (ResolvedJavaField rf : fields) { 690 if (fieldsEqual(key, rf)) { 691 return rf; 692 } 693 } 694 return null; 695 } 696 697 public Field lookupField(Set<Field> fields, ResolvedJavaField key) { 698 for (Field f : fields) { 699 if (fieldsEqual(f, key)) { 700 return f; 701 } 702 } 703 return null; 704 } 705 706 private static boolean isHiddenFromReflection(ResolvedJavaField f) { 707 if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(Throwable.class)) && f.getName().equals("backtrace")) { 708 return true; 709 } 710 if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(ConstantPool.class)) && f.getName().equals("constantPoolOop")) { 711 return true; 712 } 713 if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(Class.class)) && f.getName().equals("classLoader")) { 714 return true; 715 } 716 return false; 717 } 718 719 @Test 720 public void getInstanceFieldsTest() { 721 for (Class<?> c : classes) { 722 ResolvedJavaType type = metaAccess.lookupJavaType(c); 723 for (boolean includeSuperclasses : new boolean[]{true, false}) { 724 Set<Field> expected = getInstanceFields(c, includeSuperclasses); 725 ResolvedJavaField[] actual = type.getInstanceFields(includeSuperclasses); 726 for (Field f : expected) { 727 assertNotNull(lookupField(actual, f)); 728 } 729 for (ResolvedJavaField rf : actual) { 730 if (!isHiddenFromReflection(rf)) { 731 assertEquals(rf.toString(), lookupField(expected, rf) != null, !rf.isInternal()); 732 } 733 } 734 735 // Test stability of getInstanceFields 736 ResolvedJavaField[] actual2 = type.getInstanceFields(includeSuperclasses); 737 assertArrayEquals(actual, actual2); 738 } 739 } 740 } 741 742 @Test 743 public void getStaticFieldsTest() { 744 for (Class<?> c : classes) { 745 ResolvedJavaType type = metaAccess.lookupJavaType(c); 746 Set<Field> expected = getStaticFields(c); 747 ResolvedJavaField[] actual = type.getStaticFields(); 748 for (Field f : expected) { 749 assertNotNull(lookupField(actual, f)); 750 } 751 for (ResolvedJavaField rf : actual) { 752 if (!isHiddenFromReflection(rf)) { 753 assertEquals(lookupField(expected, rf) != null, !rf.isInternal()); 754 } 755 } 756 757 // Test stability of getStaticFields 758 ResolvedJavaField[] actual2 = type.getStaticFields(); 759 assertArrayEquals(actual, actual2); 760 } 761 } 762 763 @Test 764 public void getDeclaredMethodsTest() { 765 for (Class<?> c : classes) { 766 ResolvedJavaType type = metaAccess.lookupJavaType(c); 767 Method[] raw = c.getDeclaredMethods(); 768 Set<ResolvedJavaMethod> expected = new HashSet<>(); 769 for (Method m : raw) { 770 ResolvedJavaMethod resolvedMethod = metaAccess.lookupJavaMethod(m); 771 assertNotNull(resolvedMethod); 772 expected.add(resolvedMethod); 773 } 774 Set<ResolvedJavaMethod> actual = new HashSet<>(Arrays.asList(type.getDeclaredMethods())); 775 assertEquals(expected, actual); 776 } 777 } 778 779 static class A { 780 static String name = "foo"; 781 } 782 783 static class B extends A { 784 } 785 786 static class C { 787 } 788 789 static class D { 790 void foo() { 791 // use of assertions causes the class to have a <clinit> 792 assert getClass() != null; 793 } 794 } 795 796 static class SubD extends D { 797 798 } 799 800 @Test 801 public void getClassInitializerTest() { 802 assertNotNull(metaAccess.lookupJavaType(A.class).getClassInitializer()); 803 assertNotNull(metaAccess.lookupJavaType(D.class).getClassInitializer()); 804 assertNull(metaAccess.lookupJavaType(B.class).getClassInitializer()); 805 assertNull(metaAccess.lookupJavaType(C.class).getClassInitializer()); 806 assertNull(metaAccess.lookupJavaType(int.class).getClassInitializer()); 807 assertNull(metaAccess.lookupJavaType(void.class).getClassInitializer()); 808 } 809 810 @Test 811 public void getAnnotationsTest() { 812 for (Class<?> c : classes) { 813 ResolvedJavaType type = metaAccess.lookupJavaType(c); 814 assertArrayEquals(c.getAnnotations(), type.getAnnotations()); 815 } 816 } 817 818 @Test 819 public void getAnnotationTest() { 820 for (Class<?> c : classes) { 821 ResolvedJavaType type = metaAccess.lookupJavaType(c); 822 for (Annotation a : c.getAnnotations()) { 823 assertEquals(a, type.getAnnotation(a.annotationType())); 824 } 825 } 826 } 827 828 @Test 829 public void memberClassesTest() { 830 for (Class<?> c : classes) { 831 ResolvedJavaType type = metaAccess.lookupJavaType(c); 832 assertEquals(c.isLocalClass(), type.isLocal()); 833 assertEquals(c.isMemberClass(), type.isMember()); 834 Class<?> enclc = c.getEnclosingClass(); 835 ResolvedJavaType enclt = type.getEnclosingType(); 836 assertFalse(enclc == null ^ enclt == null); 837 if (enclc != null) { 838 assertEquals(enclt, metaAccess.lookupJavaType(enclc)); 839 } 840 } 841 } 842 843 @Test 844 public void isLeafTest() { 845 for (Class<?> c : classes) { 846 ResolvedJavaType type = metaAccess.lookupJavaType(c); 847 ResolvedJavaType arrayType = c != void.class ? metaAccess.lookupJavaType(getArrayClass(c)) : null; 848 if (c.isPrimitive()) { 849 assertTrue(type.isLeaf()); 850 assertTrue(arrayType == null || arrayType.isLeaf()); 851 } else { 852 assertTrue(c.toString(), type.isLeaf() == arrayType.isLeaf()); 853 if (!c.isArray()) { 854 assertTrue(c.toString(), type.isLeaf() == Modifier.isFinal(c.getModifiers())); 855 } 856 } 857 } 858 } 859 860 static class TrivialCloneable implements Cloneable { 861 @Override 862 protected Object clone() { 863 return new TrivialCloneable(); 864 } 865 } 866 867 @Test 868 public void isCloneableWithAllocationTest() { 869 ResolvedJavaType cloneable = metaAccess.lookupJavaType(Cloneable.class); 870 for (Class<?> c : classes) { 871 ResolvedJavaType type = metaAccess.lookupJavaType(c); 872 if (type.isCloneableWithAllocation()) { 873 // Only Cloneable types should be allocation cloneable 874 assertTrue(c.toString(), cloneable.isAssignableFrom(type)); 875 } 876 } 877 /* 878 * We can't know for sure which types should be allocation cloneable on a particular 879 * platform but assume that at least totally trivial objects should be. 880 */ 881 ResolvedJavaType trivialCloneable = metaAccess.lookupJavaType(TrivialCloneable.class); 882 assertTrue(trivialCloneable.toString(), trivialCloneable.isCloneableWithAllocation()); 883 } 884 885 @Test 886 public void findMethodTest() { 887 try { 888 ResolvedJavaMethod findFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("()V")); 889 ResolvedJavaMethod expectedFoo = metaAccess.lookupJavaMethod(D.class.getDeclaredMethod("foo")); 890 assertEquals(expectedFoo, findFoo); 891 892 ResolvedJavaMethod wrongReturnTypeFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("()I")); 893 assertNull(wrongReturnTypeFoo); 894 895 ResolvedJavaMethod wrongArgumentsFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("(I)V")); 896 assertNull(wrongArgumentsFoo); 897 898 ResolvedJavaMethod wrongNameFoo = metaAccess.lookupJavaType(D.class).findMethod("bar", metaAccess.parseMethodDescriptor("()V")); 899 assertNull(wrongNameFoo); 900 901 ResolvedJavaMethod wrongClassFoo = metaAccess.lookupJavaType(SubD.class).findMethod("foo", metaAccess.parseMethodDescriptor("()V")); 902 assertNull(wrongClassFoo); 903 } catch (NoSuchMethodException | SecurityException e) { 904 throw new RuntimeException(e); 905 } 906 } 907 908 private Method findTestMethod(Method apiMethod) { 909 String testName = apiMethod.getName() + "Test"; 910 for (Method m : getClass().getDeclaredMethods()) { 911 if (m.getName().equals(testName) && m.getAnnotation(Test.class) != null) { 912 return m; 913 } 914 } 915 return null; 916 } 917 918 // @formatter:off 919 private static final String[] untestedApiMethods = { 920 "initialize", 921 "isPrimitive", 922 "newArray", 923 "getDeclaredConstructors", 924 "isInitialized", 925 "isLinked", 926 "getJavaClass", 927 "getObjectHub", 928 "hasFinalizableSubclass", 929 "hasFinalizer", 930 "getSourceFileName", 931 "getClassFilePath", 932 "isLocal", 933 "isJavaLangObject", 934 "isMember", 935 "getElementalType", 936 "getEnclosingType", 937 "$jacocoInit", 938 "isCpiSet", 939 "getCorrespondingCpi", 940 "setCorrespondingCpi" 941 }; 942 // @formatter:on 943 944 /** 945 * Ensures that any new methods added to {@link ResolvedJavaMethod} either have a test written 946 * for them or are added to {@link #untestedApiMethods}. 947 */ 948 @Test 949 public void testCoverage() { 950 Set<String> known = new HashSet<>(Arrays.asList(untestedApiMethods)); 951 for (Method m : ResolvedJavaType.class.getDeclaredMethods()) { 952 if (findTestMethod(m) == null) { 953 assertTrue("test missing for " + m, known.contains(m.getName())); 954 } else { 955 assertFalse("test should be removed from untestedApiMethods" + m, known.contains(m.getName())); 956 } 957 } 958 } 959 }