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