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