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