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