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