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