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