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