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