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