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