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