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 @Test 587 public void resolveMethodTest() { 588 ResolvedJavaType context = metaAccess.lookupJavaType(TestResolvedJavaType.class); 589 for (Class<?> c : classes) { 590 ResolvedJavaType type = metaAccess.lookupJavaType(c); 591 if (c.isInterface()) { 592 for (Method m : c.getDeclaredMethods()) { 593 ResolvedJavaMethod resolved = metaAccess.lookupJavaMethod(m); 594 ResolvedJavaMethod impl = type.resolveMethod(resolved, context); 595 assertEquals(m.toString(), null, impl); 596 } 597 } else if (c.isPrimitive()) { 598 assertEquals("No methods expected", c.getDeclaredMethods().length, 0); 599 } else { 600 VTable vtable = getVTable(c); 601 for (Method impl : vtable.methods.values()) { 602 Set<Method> decls = findDeclarations(impl, c); 603 for (Method decl : decls) { 604 ResolvedJavaMethod m = metaAccess.lookupJavaMethod(decl); 605 if (m.isPublic()) { 606 ResolvedJavaMethod i = metaAccess.lookupJavaMethod(impl); 607 assertEquals(m.toString(), i, type.resolveMethod(m, context)); 608 } 609 } 610 } 611 } 612 } 613 } 614 615 @Test 616 public void resolveConcreteMethodTest() { 617 ResolvedJavaType context = metaAccess.lookupJavaType(TestResolvedJavaType.class); 618 for (Class<?> c : classes) { 619 ResolvedJavaType type = metaAccess.lookupJavaType(c); 620 if (c.isInterface()) { 621 for (Method m : c.getDeclaredMethods()) { 622 ResolvedJavaMethod resolved = metaAccess.lookupJavaMethod(m); 623 ResolvedJavaMethod impl = type.resolveConcreteMethod(resolved, context); 624 assertEquals(m.toString(), null, impl); 625 } 626 } else if (c.isPrimitive()) { 627 assertEquals("No methods expected", c.getDeclaredMethods().length, 0); 628 } else { 629 VTable vtable = getVTable(c); 630 for (Method impl : vtable.methods.values()) { 631 Set<Method> decls = findDeclarations(impl, c); 632 for (Method decl : decls) { 633 ResolvedJavaMethod m = metaAccess.lookupJavaMethod(decl); 634 if (m.isPublic()) { 635 ResolvedJavaMethod i = metaAccess.lookupJavaMethod(impl); 636 assertEquals(i, type.resolveConcreteMethod(m, context)); 637 } 638 } 639 } 640 for (Method m : c.getDeclaredMethods()) { 641 ResolvedJavaMethod impl = type.resolveConcreteMethod(metaAccess.lookupJavaMethod(m), context); 642 ResolvedJavaMethod expected = isAbstract(m.getModifiers()) ? null : impl; 643 assertEquals(type + " " + m.toString(), expected, impl); 644 } 645 } 646 } 647 } 648 649 @Test 650 public void findUniqueConcreteMethodTest() throws NoSuchMethodException { 651 ResolvedJavaMethod thisMethod = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("findUniqueConcreteMethodTest")); 652 ResolvedJavaMethod ucm = metaAccess.lookupJavaType(getClass()).findUniqueConcreteMethod(thisMethod).getResult(); 653 assertEquals(thisMethod, ucm); 654 } 655 656 public static Set<Field> getInstanceFields(Class<?> c, boolean includeSuperclasses) { 657 if (c.isArray() || c.isPrimitive() || c.isInterface()) { 658 return Collections.emptySet(); 659 } 660 Set<Field> result = new HashSet<>(); 661 for (Field f : c.getDeclaredFields()) { 662 if (!Modifier.isStatic(f.getModifiers())) { 663 result.add(f); 664 } 665 } 666 if (includeSuperclasses && c != Object.class) { 667 result.addAll(getInstanceFields(c.getSuperclass(), true)); 668 } 669 return result; 670 } 671 672 public static Set<Field> getStaticFields(Class<?> c) { 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 return result; 680 } 681 682 public boolean fieldsEqual(Field f, ResolvedJavaField rjf) { 683 return rjf.getDeclaringClass().equals(metaAccess.lookupJavaType(f.getDeclaringClass())) && rjf.getName().equals(f.getName()) && 684 rjf.getType().resolve(rjf.getDeclaringClass()).equals(metaAccess.lookupJavaType(f.getType())); 685 } 686 687 public ResolvedJavaField lookupField(ResolvedJavaField[] fields, Field key) { 688 for (ResolvedJavaField rf : fields) { 689 if (fieldsEqual(key, rf)) { 690 return rf; 691 } 692 } 693 return null; 694 } 695 696 public Field lookupField(Set<Field> fields, ResolvedJavaField key) { 697 for (Field f : fields) { 698 if (fieldsEqual(f, key)) { 699 return f; 700 } 701 } 702 return null; 703 } 704 705 private static boolean isHiddenFromReflection(ResolvedJavaField f) { 706 if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(Throwable.class)) && f.getName().equals("backtrace")) { 707 return true; 708 } 709 if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(ConstantPool.class)) && f.getName().equals("constantPoolOop")) { 710 return true; 711 } 712 if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(Class.class)) && f.getName().equals("classLoader")) { 713 return true; 714 } 715 return false; 716 } 717 718 @Test 719 public void getInstanceFieldsTest() { 720 for (Class<?> c : classes) { 721 ResolvedJavaType type = metaAccess.lookupJavaType(c); 722 for (boolean includeSuperclasses : new boolean[]{true, false}) { 723 Set<Field> expected = getInstanceFields(c, includeSuperclasses); 724 ResolvedJavaField[] actual = type.getInstanceFields(includeSuperclasses); 725 for (Field f : expected) { 726 assertNotNull(lookupField(actual, f)); 727 } 728 for (ResolvedJavaField rf : actual) { 729 if (!isHiddenFromReflection(rf)) { 730 assertEquals(rf.toString(), lookupField(expected, rf) != null, !rf.isInternal()); 731 } 732 } 733 734 // Test stability of getInstanceFields 735 ResolvedJavaField[] actual2 = type.getInstanceFields(includeSuperclasses); 736 assertArrayEquals(actual, actual2); 737 } 738 } 739 } 740 741 @Test 742 public void getStaticFieldsTest() { 743 for (Class<?> c : classes) { 744 ResolvedJavaType type = metaAccess.lookupJavaType(c); 745 Set<Field> expected = getStaticFields(c); 746 ResolvedJavaField[] actual = type.getStaticFields(); 747 for (Field f : expected) { 748 assertNotNull(lookupField(actual, f)); 749 } 750 for (ResolvedJavaField rf : actual) { 751 if (!isHiddenFromReflection(rf)) { 752 assertEquals(lookupField(expected, rf) != null, !rf.isInternal()); 753 } 754 } 755 756 // Test stability of getStaticFields 757 ResolvedJavaField[] actual2 = type.getStaticFields(); 758 assertArrayEquals(actual, actual2); 759 } 760 } 761 762 @Test 763 public void getDeclaredMethodsTest() { 764 for (Class<?> c : classes) { 765 ResolvedJavaType type = metaAccess.lookupJavaType(c); 766 Method[] raw = c.getDeclaredMethods(); 767 Set<ResolvedJavaMethod> expected = new HashSet<>(); 768 for (Method m : raw) { 769 ResolvedJavaMethod resolvedMethod = metaAccess.lookupJavaMethod(m); 770 assertNotNull(resolvedMethod); 771 expected.add(resolvedMethod); 772 } 773 Set<ResolvedJavaMethod> actual = new HashSet<>(Arrays.asList(type.getDeclaredMethods())); 774 assertEquals(expected, actual); 775 } 776 } 777 778 static class A { 779 static String name = "foo"; 780 } 781 782 static class B extends A { 783 } 784 785 static class C { 786 } 787 788 static class D { 789 void foo() { 790 // use of assertions causes the class to have a <clinit> 791 assert getClass() != null; 792 } 793 } 794 795 static class SubD extends D { 796 797 } 798 799 @Test 800 public void getClassInitializerTest() { 801 assertNotNull(metaAccess.lookupJavaType(A.class).getClassInitializer()); 802 assertNotNull(metaAccess.lookupJavaType(D.class).getClassInitializer()); 803 assertNull(metaAccess.lookupJavaType(B.class).getClassInitializer()); 804 assertNull(metaAccess.lookupJavaType(C.class).getClassInitializer()); 805 assertNull(metaAccess.lookupJavaType(int.class).getClassInitializer()); 806 assertNull(metaAccess.lookupJavaType(void.class).getClassInitializer()); 807 } 808 809 @Test 810 public void getAnnotationsTest() { 811 for (Class<?> c : classes) { 812 ResolvedJavaType type = metaAccess.lookupJavaType(c); 813 assertArrayEquals(c.getAnnotations(), type.getAnnotations()); 814 } 815 } 816 817 @Test 818 public void getAnnotationTest() { 819 for (Class<?> c : classes) { 820 ResolvedJavaType type = metaAccess.lookupJavaType(c); 821 for (Annotation a : c.getAnnotations()) { 822 assertEquals(a, type.getAnnotation(a.annotationType())); 823 } 824 } 825 } 826 827 @Test 828 public void memberClassesTest() { 829 for (Class<?> c : classes) { 830 ResolvedJavaType type = metaAccess.lookupJavaType(c); 831 assertEquals(c.isLocalClass(), type.isLocal()); 832 assertEquals(c.isMemberClass(), type.isMember()); 833 Class<?> enclc = c.getEnclosingClass(); 834 ResolvedJavaType enclt = type.getEnclosingType(); 835 assertFalse(enclc == null ^ enclt == null); 836 if (enclc != null) { 837 assertEquals(enclt, metaAccess.lookupJavaType(enclc)); 838 } 839 } 840 } 841 842 @Test 843 public void isTrustedInterfaceTypeTest() { 844 for (Class<?> c : classes) { 845 ResolvedJavaType type = metaAccess.lookupJavaType(c); 846 if (TrustedInterface.class.isAssignableFrom(c)) { 847 assertTrue(type.isTrustedInterfaceType()); 848 } 849 } 850 } 851 852 @Test 853 public void isLeafTest() { 854 for (Class<?> c : classes) { 855 ResolvedJavaType type = metaAccess.lookupJavaType(c); 856 ResolvedJavaType arrayType = c != void.class ? metaAccess.lookupJavaType(getArrayClass(c)) : null; 857 if (c.isPrimitive()) { 858 assertTrue(type.isLeaf()); 859 assertTrue(arrayType == null || arrayType.isLeaf()); 860 } else { 861 assertTrue(c.toString(), type.isLeaf() == arrayType.isLeaf()); 862 if (!c.isArray()) { 863 assertTrue(c.toString(), type.isLeaf() == Modifier.isFinal(c.getModifiers())); 864 } 865 } 866 } 867 } 868 869 @Test 870 public void findMethodTest() { 871 try { 872 ResolvedJavaMethod findFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("()V")); 873 ResolvedJavaMethod expectedFoo = metaAccess.lookupJavaMethod(D.class.getDeclaredMethod("foo")); 874 assertEquals(expectedFoo, findFoo); 875 876 ResolvedJavaMethod wrongReturnTypeFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("()I")); 877 assertNull(wrongReturnTypeFoo); 878 879 ResolvedJavaMethod wrongArgumentsFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("(I)V")); 880 assertNull(wrongArgumentsFoo); 881 882 ResolvedJavaMethod wrongNameFoo = metaAccess.lookupJavaType(D.class).findMethod("bar", metaAccess.parseMethodDescriptor("()V")); 883 assertNull(wrongNameFoo); 884 885 ResolvedJavaMethod wrongClassFoo = metaAccess.lookupJavaType(SubD.class).findMethod("foo", metaAccess.parseMethodDescriptor("()V")); 886 assertNull(wrongClassFoo); 887 } catch (NoSuchMethodException | SecurityException e) { 888 throw new RuntimeException(e); 889 } 890 } 891 892 private Method findTestMethod(Method apiMethod) { 893 String testName = apiMethod.getName() + "Test"; 894 for (Method m : getClass().getDeclaredMethods()) { 895 if (m.getName().equals(testName) && m.getAnnotation(Test.class) != null) { 896 return m; 897 } 898 } 899 return null; 900 } 901 902 // @formatter:off 903 private static final String[] untestedApiMethods = { 904 "initialize", 905 "isPrimitive", 906 "newArray", 907 "getDeclaredConstructors", 908 "isInitialized", 909 "isLinked", 910 "getJavaClass", 911 "getObjectHub", 912 "hasFinalizableSubclass", 913 "hasFinalizer", 914 "getSourceFileName", 915 "getClassFilePath", 916 "isLocal", 917 "isJavaLangObject", 918 "isMember", 919 "getElementalType", 920 "getEnclosingType", 921 "$jacocoInit", 922 "isCpiSet", 923 "getCorrespondingCpi", 924 "setCorrespondingCpi" 925 }; 926 // @formatter:on 927 928 /** 929 * Ensures that any new methods added to {@link ResolvedJavaMethod} either have a test written 930 * for them or are added to {@link #untestedApiMethods}. 931 */ 932 @Test 933 public void testCoverage() { 934 Set<String> known = new HashSet<>(Arrays.asList(untestedApiMethods)); 935 for (Method m : ResolvedJavaType.class.getDeclaredMethods()) { 936 if (findTestMethod(m) == null) { 937 assertTrue("test missing for " + m, known.contains(m.getName())); 938 } else { 939 assertFalse("test should be removed from untestedApiMethods" + m, known.contains(m.getName())); 940 } 941 } 942 } 943 }