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