1 /* 2 * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /** 25 * @test 26 * @requires vm.jvmci 27 * @library ../../../../../ 28 * @modules java.base/jdk.internal.reflect 29 * jdk.internal.vm.ci/jdk.vm.ci.meta 30 * jdk.internal.vm.ci/jdk.vm.ci.runtime 31 * jdk.internal.vm.ci/jdk.vm.ci.common 32 * java.base/jdk.internal.misc 33 * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -Djvmci.Compiler=null jdk.vm.ci.runtime.test.TestResolvedJavaType 34 */ 35 36 package jdk.vm.ci.runtime.test; 37 38 import static java.lang.reflect.Modifier.isAbstract; 39 import static java.lang.reflect.Modifier.isFinal; 40 import static java.lang.reflect.Modifier.isPrivate; 41 import static java.lang.reflect.Modifier.isProtected; 42 import static java.lang.reflect.Modifier.isPublic; 43 import static java.lang.reflect.Modifier.isStatic; 44 import static org.junit.Assert.assertArrayEquals; 45 import static org.junit.Assert.assertEquals; 46 import static org.junit.Assert.assertFalse; 47 import static org.junit.Assert.assertNotNull; 48 import static org.junit.Assert.assertNull; 49 import static org.junit.Assert.assertTrue; 50 51 import java.lang.annotation.Annotation; 52 import java.lang.invoke.MethodHandles.Lookup; 53 import java.lang.reflect.AccessibleObject; 54 import java.lang.reflect.Constructor; 55 import java.lang.reflect.Field; 56 import java.lang.reflect.Method; 57 import java.lang.reflect.Modifier; 58 import java.util.Arrays; 59 import java.util.Collections; 60 import java.util.function.Supplier; 61 import java.util.HashMap; 62 import java.util.HashSet; 63 import java.util.Map; 64 import java.util.Set; 65 66 import org.junit.Test; 67 68 import jdk.internal.reflect.ConstantPool; 69 import jdk.vm.ci.common.JVMCIError; 70 import jdk.vm.ci.meta.Assumptions.AssumptionResult; 71 import jdk.vm.ci.meta.JavaConstant; 72 import jdk.vm.ci.meta.JavaKind; 73 import jdk.vm.ci.meta.ResolvedJavaField; 74 import jdk.vm.ci.meta.ResolvedJavaMethod; 75 import jdk.vm.ci.meta.ResolvedJavaType; 76 77 /** 78 * Tests for {@link ResolvedJavaType}. 79 */ 80 @SuppressWarnings("unchecked") 81 public class TestResolvedJavaType extends TypeUniverse { 82 private static final Class<? extends Annotation> SIGNATURE_POLYMORPHIC_CLASS = findPolymorphicSignatureClass(); 83 84 public TestResolvedJavaType() { 85 } 86 87 private static Class<? extends Annotation> findPolymorphicSignatureClass() { 88 Class<? extends Annotation> signaturePolyAnnotation = null; 89 try { 90 for (Class<?> clazz : TestResolvedJavaType.class.getClassLoader().loadClass("java.lang.invoke.MethodHandle").getDeclaredClasses()) { 91 if (clazz.getName().endsWith("PolymorphicSignature") && Annotation.class.isAssignableFrom(clazz)) { 92 signaturePolyAnnotation = (Class<? extends Annotation>) clazz; 93 break; 94 } 95 } 96 } catch (Throwable e) { 97 throw new AssertionError("Could not find annotation PolymorphicSignature in java.lang.invoke.MethodHandle", e); 98 } 99 assertNotNull(signaturePolyAnnotation); 100 return signaturePolyAnnotation; 101 } 102 103 @Test 104 public void findInstanceFieldWithOffsetTest() { 105 for (Class<?> c : classes) { 106 ResolvedJavaType type = metaAccess.lookupJavaType(c); 107 Set<Field> reflectionFields = getInstanceFields(c, true); 108 for (Field f : reflectionFields) { 109 ResolvedJavaField rf = lookupField(type.getInstanceFields(true), f); 110 assertNotNull(rf); 111 long offset = isStatic(f.getModifiers()) ? unsafe.staticFieldOffset(f) : unsafe.objectFieldOffset(f); 112 ResolvedJavaField result = type.findInstanceFieldWithOffset(offset, rf.getJavaKind()); 113 assertNotNull(result); 114 assertTrue(fieldsEqual(f, result)); 115 } 116 } 117 } 118 119 @Test 120 public void isInterfaceTest() { 121 for (Class<?> c : classes) { 122 ResolvedJavaType type = metaAccess.lookupJavaType(c); 123 boolean expected = c.isInterface(); 124 boolean actual = type.isInterface(); 125 assertEquals(expected, actual); 126 } 127 } 128 129 @Test 130 public void isEnumTest() { 131 for (Class<?> c : classes) { 132 ResolvedJavaType type = metaAccess.lookupJavaType(c); 133 boolean expected = c.isEnum(); 134 boolean actual = type.isEnum(); 135 assertEquals(expected, actual); 136 } 137 } 138 139 @Test 140 public void isInstanceClassTest() { 141 for (Class<?> c : classes) { 142 ResolvedJavaType type = metaAccess.lookupJavaType(c); 143 boolean expected = !c.isArray() && !c.isPrimitive() && !c.isInterface(); 144 boolean actual = type.isInstanceClass(); 145 assertEquals(expected, actual); 146 } 147 } 148 149 @Test 150 public void isArrayTest() { 151 for (Class<?> c : classes) { 152 ResolvedJavaType type = metaAccess.lookupJavaType(c); 153 boolean expected = c.isArray(); 154 boolean actual = type.isArray(); 155 assertEquals(expected, actual); 156 } 157 } 158 159 @Test 160 public void getHostClassTest() { 161 for (Class<?> c : classes) { 162 ResolvedJavaType type = metaAccess.lookupJavaType(c); 163 ResolvedJavaType host = type.getHostClass(); 164 assertNull(host); 165 } 166 167 class LocalClass {} 168 Cloneable clone = new Cloneable() {}; 169 assertNull(metaAccess.lookupJavaType(LocalClass.class).getHostClass()); 170 assertNull(metaAccess.lookupJavaType(clone.getClass()).getHostClass()); 171 172 Supplier<Runnable> lambda = () -> () -> System.out.println("run"); 173 ResolvedJavaType lambdaType = metaAccess.lookupJavaType(lambda.getClass()); 174 ResolvedJavaType nestedLambdaType = metaAccess.lookupJavaType(lambda.get().getClass()); 175 assertNotNull(lambdaType.getHostClass()); 176 assertNotNull(nestedLambdaType.getHostClass()); 177 assertEquals(lambdaType.getHostClass(), nestedLambdaType.getHostClass()); 178 } 179 180 @Test 181 public void getModifiersTest() { 182 for (Class<?> c : classes) { 183 ResolvedJavaType type = metaAccess.lookupJavaType(c); 184 int mask = Modifier.classModifiers() & ~Modifier.STATIC; 185 int expected = c.getModifiers() & mask; 186 int actual = type.getModifiers() & mask; 187 Class<?> elementalType = c; 188 while (elementalType.isArray()) { 189 elementalType = elementalType.getComponentType(); 190 } 191 if (elementalType.isMemberClass()) { 192 // member class get their modifiers from the inner-class attribute in the JVM and 193 // from the classfile header in jvmci 194 expected &= ~(Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED); 195 actual &= ~(Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED); 196 } 197 assertEquals(String.format("%s: 0x%x != 0x%x", type, expected, actual), expected, actual); 198 } 199 } 200 201 @Test 202 public void isAssignableFromTest() { 203 Class<?>[] all = classes.toArray(new Class<?>[classes.size()]); 204 for (int i = 0; i < all.length; i++) { 205 Class<?> c1 = all[i]; 206 for (int j = i; j < all.length; j++) { 207 Class<?> c2 = all[j]; 208 ResolvedJavaType t1 = metaAccess.lookupJavaType(c1); 209 ResolvedJavaType t2 = metaAccess.lookupJavaType(c2); 210 boolean expected = c1.isAssignableFrom(c2); 211 boolean actual = t1.isAssignableFrom(t2); 212 assertEquals(expected, actual); 213 if (expected && t1 != t2) { 214 assertFalse(t2.isAssignableFrom(t1)); 215 } 216 } 217 } 218 } 219 220 @Test 221 public void isInstanceTest() { 222 for (ConstantValue cv : constants()) { 223 JavaConstant c = cv.value; 224 if (c.getJavaKind() == JavaKind.Object && !c.isNull()) { 225 ResolvedJavaType cType = metaAccess.lookupJavaType(c); 226 for (ResolvedJavaType t : javaTypes) { 227 if (t.isAssignableFrom(cType)) { 228 assertTrue(t.isInstance(c)); 229 } else { 230 assertFalse(t.isInstance(c)); 231 } 232 } 233 } 234 } 235 } 236 237 @Test 238 public void getSuperclassTest() { 239 for (Class<?> c : classes) { 240 ResolvedJavaType type = metaAccess.lookupJavaType(c); 241 Class<?> expected = c.getSuperclass(); 242 ResolvedJavaType actual = type.getSuperclass(); 243 if (expected == null) { 244 assertTrue(actual == null); 245 } else { 246 assertNotNull(actual); 247 assertTrue(actual.equals(metaAccess.lookupJavaType(expected))); 248 } 249 } 250 } 251 252 @Test 253 public void getInterfacesTest() { 254 for (Class<?> c : classes) { 255 ResolvedJavaType type = metaAccess.lookupJavaType(c); 256 Class<?>[] expected = c.getInterfaces(); 257 ResolvedJavaType[] actual = type.getInterfaces(); 258 assertEquals(expected.length, actual.length); 259 for (int i = 0; i < expected.length; i++) { 260 assertTrue(actual[i].equals(metaAccess.lookupJavaType(expected[i]))); 261 } 262 } 263 } 264 265 public Class<?> getSupertype(Class<?> c) { 266 assert !c.isPrimitive(); 267 if (c.isArray()) { 268 Class<?> componentType = c.getComponentType(); 269 if (componentType.isPrimitive() || componentType == Object.class) { 270 return Object.class; 271 } 272 return getArrayClass(getSupertype(componentType)); 273 } 274 if (c.isInterface()) { 275 return Object.class; 276 } 277 return c.getSuperclass(); 278 } 279 280 public Class<?> findLeastCommonAncestor(Class<?> c1Initial, Class<?> c2Initial) { 281 if (c1Initial.isPrimitive() || c2Initial.isPrimitive()) { 282 return null; 283 } else { 284 Class<?> c1 = c1Initial; 285 Class<?> c2 = c2Initial; 286 while (true) { 287 if (c1.isAssignableFrom(c2)) { 288 return c1; 289 } 290 if (c2.isAssignableFrom(c1)) { 291 return c2; 292 } 293 c1 = getSupertype(c1); 294 c2 = getSupertype(c2); 295 } 296 } 297 } 298 299 @Test 300 public void findLeastCommonAncestorTest() { 301 Class<?>[] all = classes.toArray(new Class<?>[classes.size()]); 302 for (int i = 0; i < all.length; i++) { 303 Class<?> c1 = all[i]; 304 for (int j = i; j < all.length; j++) { 305 Class<?> c2 = all[j]; 306 ResolvedJavaType t1 = metaAccess.lookupJavaType(c1); 307 ResolvedJavaType t2 = metaAccess.lookupJavaType(c2); 308 Class<?> expected = findLeastCommonAncestor(c1, c2); 309 ResolvedJavaType actual = t1.findLeastCommonAncestor(t2); 310 if (expected == null) { 311 assertTrue(actual == null); 312 } else { 313 assertNotNull(actual); 314 assertTrue(actual.equals(metaAccess.lookupJavaType(expected))); 315 } 316 } 317 } 318 } 319 320 private static class Base { 321 } 322 323 abstract static class Abstract1 extends Base { 324 } 325 326 interface Interface1 { 327 } 328 329 static class Concrete1 extends Abstract1 { 330 } 331 332 static class Concrete2 extends Abstract1 implements Interface1 { 333 } 334 335 static class Concrete3 extends Concrete2 { 336 } 337 338 static final class Final1 extends Abstract1 { 339 } 340 341 abstract static class Abstract4 extends Concrete3 { 342 } 343 344 void checkConcreteSubtype(ResolvedJavaType type, ResolvedJavaType expected) { 345 AssumptionResult<ResolvedJavaType> leafConcreteSubtype = type.findLeafConcreteSubtype(); 346 if (leafConcreteSubtype == null) { 347 // findLeafConcreteSubtype() is conservative 348 } else { 349 if (expected == null) { 350 assertNull(leafConcreteSubtype); 351 } else { 352 assertTrue(leafConcreteSubtype.getResult().equals(expected)); 353 } 354 assertTrue(!type.isLeaf() || leafConcreteSubtype.isAssumptionFree()); 355 } 356 357 if (!type.isArray()) { 358 ResolvedJavaType arrayType = type.getArrayClass(); 359 AssumptionResult<ResolvedJavaType> arraySubtype = arrayType.findLeafConcreteSubtype(); 360 if (arraySubtype != null) { 361 assertEquals(arraySubtype.getResult(), arrayType); 362 } else { 363 // findLeafConcreteSubtype() method is conservative 364 } 365 } 366 } 367 368 @Test 369 public void findLeafConcreteSubtypeTest() { 370 ResolvedJavaType base = metaAccess.lookupJavaType(Base.class); 371 checkConcreteSubtype(base, base); 372 373 ResolvedJavaType a1 = metaAccess.lookupJavaType(Abstract1.class); 374 ResolvedJavaType c1 = metaAccess.lookupJavaType(Concrete1.class); 375 376 checkConcreteSubtype(base, null); 377 checkConcreteSubtype(a1, c1); 378 checkConcreteSubtype(c1, c1); 379 380 ResolvedJavaType i1 = metaAccess.lookupJavaType(Interface1.class); 381 ResolvedJavaType c2 = metaAccess.lookupJavaType(Concrete2.class); 382 383 checkConcreteSubtype(base, null); 384 checkConcreteSubtype(a1, null); 385 checkConcreteSubtype(c1, c1); 386 checkConcreteSubtype(i1, c2); 387 checkConcreteSubtype(c2, c2); 388 389 ResolvedJavaType c3 = metaAccess.lookupJavaType(Concrete3.class); 390 checkConcreteSubtype(c2, null); 391 checkConcreteSubtype(c3, c3); 392 393 ResolvedJavaType a4 = metaAccess.lookupJavaType(Abstract4.class); 394 checkConcreteSubtype(c3, null); 395 checkConcreteSubtype(a4, null); 396 397 ResolvedJavaType a1a = metaAccess.lookupJavaType(Abstract1[].class); 398 checkConcreteSubtype(a1a, null); 399 ResolvedJavaType i1a = metaAccess.lookupJavaType(Interface1[].class); 400 checkConcreteSubtype(i1a, null); 401 ResolvedJavaType c1a = metaAccess.lookupJavaType(Concrete1[].class); 402 checkConcreteSubtype(c1a, c1a); 403 ResolvedJavaType f1a = metaAccess.lookupJavaType(Final1[].class); 404 checkConcreteSubtype(f1a, f1a); 405 406 ResolvedJavaType obja = metaAccess.lookupJavaType(Object[].class); 407 checkConcreteSubtype(obja, null); 408 409 ResolvedJavaType inta = metaAccess.lookupJavaType(int[].class); 410 checkConcreteSubtype(inta, inta); 411 } 412 413 interface NoImplementor { 414 } 415 416 interface SingleImplementorInterface { 417 } 418 419 static class SingleConcreteImplementor implements SingleImplementorInterface { 420 } 421 422 interface SingleAbstractImplementorInterface { 423 } 424 425 abstract static class SingleAbstractImplementor implements SingleAbstractImplementorInterface { 426 } 427 428 interface MultiImplementorInterface { 429 } 430 431 static class ConcreteImplementor1 implements MultiImplementorInterface { 432 } 433 434 static class ConcreteImplementor2 implements MultiImplementorInterface { 435 } 436 437 interface MultipleAbstractImplementorInterface { 438 } 439 440 abstract static class MultiAbstractImplementor1 implements MultipleAbstractImplementorInterface { 441 } 442 443 abstract static class MultiAbstractImplementor2 implements MultipleAbstractImplementorInterface { 444 } 445 446 interface SingleAbstractImplementorInterface2 { 447 } 448 449 interface ExtendedSingleImplementorInterface { 450 } 451 452 abstract static class SingleAbstractImplementor2 implements SingleAbstractImplementorInterface2 { 453 } 454 455 static class ConcreteTransitiveImplementor1 extends SingleAbstractImplementor2 implements ExtendedSingleImplementorInterface { 456 } 457 458 static class ConcreteTransitiveImplementor2 extends SingleAbstractImplementor2 implements ExtendedSingleImplementorInterface { 459 } 460 461 @Test 462 public void getSingleImplementorTest() { 463 ResolvedJavaType iNi = metaAccess.lookupJavaType(NoImplementor.class); 464 assertNull(iNi.getSingleImplementor()); 465 466 ResolvedJavaType iSi = metaAccess.lookupJavaType(SingleImplementorInterface.class); 467 ResolvedJavaType cSi = metaAccess.lookupJavaType(SingleConcreteImplementor.class); 468 assertEquals(cSi, iSi.getSingleImplementor()); 469 470 ResolvedJavaType iSai = metaAccess.lookupJavaType(SingleAbstractImplementorInterface.class); 471 ResolvedJavaType aSai = metaAccess.lookupJavaType(SingleAbstractImplementor.class); 472 assertEquals(aSai, iSai.getSingleImplementor()); 473 474 ResolvedJavaType iMi = metaAccess.lookupJavaType(MultiImplementorInterface.class); 475 metaAccess.lookupJavaType(ConcreteImplementor1.class); 476 metaAccess.lookupJavaType(ConcreteImplementor2.class); 477 assertEquals(iMi, iMi.getSingleImplementor()); 478 479 ResolvedJavaType iMai = metaAccess.lookupJavaType(MultipleAbstractImplementorInterface.class); 480 metaAccess.lookupJavaType(MultiAbstractImplementor1.class); 481 metaAccess.lookupJavaType(MultiAbstractImplementor2.class); 482 assertEquals(iMai, iMai.getSingleImplementor()); 483 484 ResolvedJavaType iSai2 = metaAccess.lookupJavaType(SingleAbstractImplementorInterface2.class); 485 ResolvedJavaType aSai2 = metaAccess.lookupJavaType(SingleAbstractImplementor2.class); 486 metaAccess.lookupJavaType(ConcreteTransitiveImplementor1.class); 487 metaAccess.lookupJavaType(ConcreteTransitiveImplementor2.class); 488 assertEquals(aSai2, iSai2.getSingleImplementor()); 489 490 for (Class<?> c : classes) { 491 ResolvedJavaType type = metaAccess.lookupJavaType(c); 492 try { 493 type.getSingleImplementor(); 494 if (!c.isInterface()) { 495 throw new AssertionError("Expected exception for calling getSingleImplmentor on " + c.getName()); 496 } 497 } catch (JVMCIError e) { 498 if (c.isInterface()) { 499 throw new AssertionError("Unexpected exception", e); 500 } 501 } 502 } 503 } 504 505 @Test(expected = JVMCIError.class) 506 public void getSingleImplementorTestClassReceiver() { 507 ResolvedJavaType base = metaAccess.lookupJavaType(Base.class); 508 base.getSingleImplementor(); 509 } 510 511 @Test(expected = JVMCIError.class) 512 public void getSingleImplementorTestPrimitiveReceiver() { 513 ResolvedJavaType primitive = metaAccess.lookupJavaType(int.class); 514 primitive.getSingleImplementor(); 515 } 516 517 @Test 518 public void getComponentTypeTest() { 519 for (Class<?> c : classes) { 520 ResolvedJavaType type = metaAccess.lookupJavaType(c); 521 Class<?> expected = c.getComponentType(); 522 ResolvedJavaType actual = type.getComponentType(); 523 if (expected == null) { 524 assertNull(actual); 525 } else { 526 assertTrue(actual.equals(metaAccess.lookupJavaType(expected))); 527 } 528 } 529 } 530 531 @Test 532 public void getArrayClassTest() { 533 for (Class<?> c : classes) { 534 if (c != void.class) { 535 ResolvedJavaType type = metaAccess.lookupJavaType(c); 536 Class<?> expected = getArrayClass(c); 537 ResolvedJavaType actual = type.getArrayClass(); 538 assertTrue(actual.equals(metaAccess.lookupJavaType(expected))); 539 } 540 } 541 } 542 543 static class Declarations { 544 545 final Method implementation; 546 final Set<Method> declarations; 547 548 Declarations(Method impl) { 549 this.implementation = impl; 550 declarations = new HashSet<>(); 551 } 552 } 553 554 /** 555 * See <a href="http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.4.5">Method 556 * overriding</a>. 557 */ 558 static boolean isOverriderOf(Method impl, Method m) { 559 if (!isPrivate(m.getModifiers()) && !isFinal(m.getModifiers())) { 560 if (m.getName().equals(impl.getName())) { 561 if (m.getReturnType() == impl.getReturnType()) { 562 if (Arrays.equals(m.getParameterTypes(), impl.getParameterTypes())) { 563 if (isPublic(m.getModifiers()) || isProtected(m.getModifiers())) { 564 // m is public or protected 565 return isPublic(impl.getModifiers()) || isProtected(impl.getModifiers()); 566 } else { 567 // m is package-private 568 return impl.getDeclaringClass().getPackage() == m.getDeclaringClass().getPackage(); 569 } 570 } 571 } 572 } 573 } 574 return false; 575 } 576 577 static final Map<Class<?>, VTable> vtables = new HashMap<>(); 578 579 static class VTable { 580 581 final Map<NameAndSignature, Method> methods = new HashMap<>(); 582 } 583 584 static synchronized VTable getVTable(Class<?> c) { 585 VTable vtable = vtables.get(c); 586 if (vtable == null) { 587 vtable = new VTable(); 588 if (c != Object.class) { 589 VTable superVtable = getVTable(c.getSuperclass()); 590 vtable.methods.putAll(superVtable.methods); 591 } 592 for (Method m : c.getDeclaredMethods()) { 593 if (!isStatic(m.getModifiers()) && !isPrivate(m.getModifiers())) { 594 if (isAbstract(m.getModifiers())) { 595 // A subclass makes a concrete method in a superclass abstract 596 vtable.methods.remove(new NameAndSignature(m)); 597 } else { 598 vtable.methods.put(new NameAndSignature(m), m); 599 } 600 } 601 } 602 vtables.put(c, vtable); 603 } 604 return vtable; 605 } 606 607 static Set<Method> findDeclarations(Method impl, Class<?> c) { 608 Set<Method> declarations = new HashSet<>(); 609 NameAndSignature implSig = new NameAndSignature(impl); 610 if (c != null) { 611 for (Method m : c.getDeclaredMethods()) { 612 if (new NameAndSignature(m).equals(implSig)) { 613 declarations.add(m); 614 break; 615 } 616 } 617 if (!c.isInterface()) { 618 declarations.addAll(findDeclarations(impl, c.getSuperclass())); 619 } 620 for (Class<?> i : c.getInterfaces()) { 621 declarations.addAll(findDeclarations(impl, i)); 622 } 623 } 624 return declarations; 625 } 626 627 @Test 628 public void resolveMethodTest() { 629 ResolvedJavaType context = metaAccess.lookupJavaType(TestResolvedJavaType.class); 630 for (Class<?> c : classes) { 631 ResolvedJavaType type = metaAccess.lookupJavaType(c); 632 if (c.isInterface()) { 633 for (Method m : c.getDeclaredMethods()) { 634 ResolvedJavaMethod resolved = metaAccess.lookupJavaMethod(m); 635 ResolvedJavaMethod impl = type.resolveMethod(resolved, context); 636 assertEquals(m.toString(), null, impl); 637 } 638 } else if (c.isPrimitive()) { 639 assertEquals("No methods expected", c.getDeclaredMethods().length, 0); 640 } else { 641 VTable vtable = getVTable(c); 642 for (Method impl : vtable.methods.values()) { 643 Set<Method> decls = findDeclarations(impl, c); 644 for (Method decl : decls) { 645 ResolvedJavaMethod m = metaAccess.lookupJavaMethod(decl); 646 if (m.isPublic()) { 647 ResolvedJavaMethod resolvedmethod = type.resolveMethod(m, context); 648 if (isSignaturePolymorphic(m)) { 649 // Signature polymorphic methods must not be resolved 650 assertNull(resolvedmethod); 651 } else { 652 ResolvedJavaMethod i = metaAccess.lookupJavaMethod(impl); 653 assertEquals(m.toString(), i, resolvedmethod); 654 } 655 } 656 } 657 } 658 } 659 } 660 } 661 662 @Test 663 public void resolveConcreteMethodTest() { 664 ResolvedJavaType context = metaAccess.lookupJavaType(TestResolvedJavaType.class); 665 for (Class<?> c : classes) { 666 ResolvedJavaType type = metaAccess.lookupJavaType(c); 667 if (c.isInterface()) { 668 for (Method m : c.getDeclaredMethods()) { 669 ResolvedJavaMethod resolved = metaAccess.lookupJavaMethod(m); 670 ResolvedJavaMethod impl = type.resolveConcreteMethod(resolved, context); 671 assertEquals(m.toString(), null, impl); 672 } 673 } else if (c.isPrimitive()) { 674 assertEquals("No methods expected", c.getDeclaredMethods().length, 0); 675 } else { 676 VTable vtable = getVTable(c); 677 for (Method impl : vtable.methods.values()) { 678 Set<Method> decls = findDeclarations(impl, c); 679 for (Method decl : decls) { 680 ResolvedJavaMethod m = metaAccess.lookupJavaMethod(decl); 681 if (m.isPublic()) { 682 ResolvedJavaMethod resolvedMethod = type.resolveConcreteMethod(m, context); 683 if (isSignaturePolymorphic(m)) { 684 // Signature polymorphic methods must not be resolved 685 assertNull(String.format("Got: %s", resolvedMethod), resolvedMethod); 686 } else { 687 ResolvedJavaMethod i = metaAccess.lookupJavaMethod(impl); 688 assertEquals(i, resolvedMethod); 689 } 690 } 691 } 692 } 693 for (Method m : c.getDeclaredMethods()) { 694 ResolvedJavaMethod impl = type.resolveConcreteMethod(metaAccess.lookupJavaMethod(m), context); 695 ResolvedJavaMethod expected = isAbstract(m.getModifiers()) ? null : impl; 696 assertEquals(type + " " + m.toString(), expected, impl); 697 } 698 } 699 } 700 } 701 702 @Test 703 public void findUniqueConcreteMethodTest() throws NoSuchMethodException { 704 ResolvedJavaMethod thisMethod = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("findUniqueConcreteMethodTest")); 705 ResolvedJavaMethod ucm = metaAccess.lookupJavaType(getClass()).findUniqueConcreteMethod(thisMethod).getResult(); 706 assertEquals(thisMethod, ucm); 707 } 708 709 public static Set<Field> getInstanceFields(Class<?> c, boolean includeSuperclasses) { 710 if (c.isArray() || c.isPrimitive() || c.isInterface()) { 711 return Collections.emptySet(); 712 } 713 Set<Field> result = new HashSet<>(); 714 for (Field f : c.getDeclaredFields()) { 715 if (!Modifier.isStatic(f.getModifiers())) { 716 result.add(f); 717 } 718 } 719 if (includeSuperclasses && c != Object.class) { 720 result.addAll(getInstanceFields(c.getSuperclass(), true)); 721 } 722 return result; 723 } 724 725 public static Set<Field> getStaticFields(Class<?> c) { 726 Set<Field> result = new HashSet<>(); 727 for (Field f : c.getDeclaredFields()) { 728 if (Modifier.isStatic(f.getModifiers())) { 729 result.add(f); 730 } 731 } 732 return result; 733 } 734 735 public boolean fieldsEqual(Field f, ResolvedJavaField rjf) { 736 return rjf.getDeclaringClass().equals(metaAccess.lookupJavaType(f.getDeclaringClass())) && rjf.getName().equals(f.getName()) && 737 rjf.getType().resolve(rjf.getDeclaringClass()).equals(metaAccess.lookupJavaType(f.getType())); 738 } 739 740 public ResolvedJavaField lookupField(ResolvedJavaField[] fields, Field key) { 741 for (ResolvedJavaField rf : fields) { 742 if (fieldsEqual(key, rf)) { 743 return rf; 744 } 745 } 746 return null; 747 } 748 749 public Field lookupField(Set<Field> fields, ResolvedJavaField key) { 750 for (Field f : fields) { 751 if (fieldsEqual(f, key)) { 752 return f; 753 } 754 } 755 return null; 756 } 757 758 private static boolean isHiddenFromReflection(ResolvedJavaField f) { 759 if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(Throwable.class)) && f.getName().equals("backtrace")) { 760 return true; 761 } 762 if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(ConstantPool.class)) && f.getName().equals("constantPoolOop")) { 763 return true; 764 } 765 if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(Class.class)) && f.getName().equals("classLoader")) { 766 return true; 767 } 768 if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(Lookup.class))) { 769 return f.getName().equals("allowedModes") || f.getName().equals("lookupClass"); 770 } 771 if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(ClassLoader.class)) || 772 f.getDeclaringClass().equals(metaAccess.lookupJavaType(AccessibleObject.class)) || 773 f.getDeclaringClass().equals(metaAccess.lookupJavaType(Constructor.class)) || 774 f.getDeclaringClass().equals(metaAccess.lookupJavaType(Field.class)) || 775 f.getDeclaringClass().equals(metaAccess.lookupJavaType(Method.class))) { 776 return true; 777 } 778 return false; 779 } 780 781 @Test 782 public void getInstanceFieldsTest() { 783 for (Class<?> c : classes) { 784 ResolvedJavaType type = metaAccess.lookupJavaType(c); 785 for (boolean includeSuperclasses : new boolean[]{true, false}) { 786 Set<Field> expected = getInstanceFields(c, includeSuperclasses); 787 ResolvedJavaField[] actual = type.getInstanceFields(includeSuperclasses); 788 for (Field f : expected) { 789 assertNotNull(lookupField(actual, f)); 790 } 791 for (ResolvedJavaField rf : actual) { 792 if (!isHiddenFromReflection(rf)) { 793 assertEquals(rf.toString(), lookupField(expected, rf) != null, !rf.isInternal()); 794 } 795 } 796 797 // Test stability of getInstanceFields 798 ResolvedJavaField[] actual2 = type.getInstanceFields(includeSuperclasses); 799 assertArrayEquals(actual, actual2); 800 } 801 } 802 } 803 804 @Test 805 public void getStaticFieldsTest() { 806 for (Class<?> c : classes) { 807 ResolvedJavaType type = metaAccess.lookupJavaType(c); 808 Set<Field> expected = getStaticFields(c); 809 ResolvedJavaField[] actual = type.getStaticFields(); 810 for (Field f : expected) { 811 assertNotNull(lookupField(actual, f)); 812 } 813 for (ResolvedJavaField rf : actual) { 814 if (!isHiddenFromReflection(rf)) { 815 assertEquals(lookupField(expected, rf) != null, !rf.isInternal()); 816 } 817 } 818 819 // Test stability of getStaticFields 820 ResolvedJavaField[] actual2 = type.getStaticFields(); 821 assertArrayEquals(actual, actual2); 822 } 823 } 824 825 @Test 826 public void getDeclaredMethodsTest() { 827 for (Class<?> c : classes) { 828 ResolvedJavaType type = metaAccess.lookupJavaType(c); 829 Method[] raw = c.getDeclaredMethods(); 830 Set<ResolvedJavaMethod> expected = new HashSet<>(); 831 for (Method m : raw) { 832 ResolvedJavaMethod resolvedMethod = metaAccess.lookupJavaMethod(m); 833 assertNotNull(resolvedMethod); 834 expected.add(resolvedMethod); 835 } 836 Set<ResolvedJavaMethod> actual = new HashSet<>(Arrays.asList(type.getDeclaredMethods())); 837 assertEquals(expected, actual); 838 } 839 } 840 841 static class A { 842 static String name = "foo"; 843 } 844 845 static class B extends A { 846 } 847 848 static class C { 849 } 850 851 static class D { 852 void foo() { 853 // use of assertions causes the class to have a <clinit> 854 assert getClass() != null; 855 } 856 } 857 858 static class SubD extends D { 859 860 } 861 862 private static ResolvedJavaMethod getClassInitializer(Class<?> c) { 863 ResolvedJavaMethod clinit = metaAccess.lookupJavaType(c).getClassInitializer(); 864 if (clinit != null) { 865 assertEquals(0, clinit.getAnnotations().length); 866 assertEquals(0, clinit.getDeclaredAnnotations().length); 867 } 868 return clinit; 869 } 870 871 @Test 872 public void getClassInitializerTest() { 873 assertNotNull(getClassInitializer(A.class)); 874 assertNotNull(getClassInitializer(D.class)); 875 assertNull(getClassInitializer(B.class)); 876 assertNull(getClassInitializer(C.class)); 877 assertNull(getClassInitializer(int.class)); 878 assertNull(getClassInitializer(void.class)); 879 for (Class<?> c : classes) { 880 getClassInitializer(c); 881 } 882 } 883 884 @Test 885 public void getAnnotationsTest() { 886 for (Class<?> c : classes) { 887 ResolvedJavaType type = metaAccess.lookupJavaType(c); 888 assertArrayEquals(c.getAnnotations(), type.getAnnotations()); 889 } 890 } 891 892 @Test 893 public void getAnnotationTest() { 894 for (Class<?> c : classes) { 895 ResolvedJavaType type = metaAccess.lookupJavaType(c); 896 for (Annotation a : c.getAnnotations()) { 897 assertEquals(a, type.getAnnotation(a.annotationType())); 898 } 899 } 900 } 901 902 @Test 903 public void memberClassesTest() { 904 for (Class<?> c : classes) { 905 ResolvedJavaType type = metaAccess.lookupJavaType(c); 906 assertEquals(c.isLocalClass(), type.isLocal()); 907 assertEquals(c.isMemberClass(), type.isMember()); 908 Class<?> enclc = c.getEnclosingClass(); 909 ResolvedJavaType enclt = type.getEnclosingType(); 910 assertFalse(enclc == null ^ enclt == null); 911 if (enclc != null) { 912 assertEquals(enclt, metaAccess.lookupJavaType(enclc)); 913 } 914 } 915 } 916 917 @Test 918 public void isLeafTest() { 919 for (Class<?> c : classes) { 920 ResolvedJavaType type = metaAccess.lookupJavaType(c); 921 ResolvedJavaType arrayType = c != void.class ? metaAccess.lookupJavaType(getArrayClass(c)) : null; 922 if (c.isPrimitive()) { 923 assertTrue(type.isLeaf()); 924 assertTrue(arrayType == null || arrayType.isLeaf()); 925 } else { 926 assertTrue(c.toString(), type.isLeaf() == arrayType.isLeaf()); 927 if (!c.isArray()) { 928 assertTrue(c.toString(), type.isLeaf() == Modifier.isFinal(c.getModifiers())); 929 } 930 } 931 } 932 } 933 934 static class TrivialCloneable implements Cloneable { 935 @Override 936 protected Object clone() { 937 return new TrivialCloneable(); 938 } 939 } 940 941 @Test 942 public void isCloneableWithAllocationTest() { 943 ResolvedJavaType cloneable = metaAccess.lookupJavaType(Cloneable.class); 944 for (Class<?> c : classes) { 945 ResolvedJavaType type = metaAccess.lookupJavaType(c); 946 if (type.isCloneableWithAllocation()) { 947 // Only Cloneable types should be allocation cloneable 948 assertTrue(c.toString(), cloneable.isAssignableFrom(type)); 949 } 950 } 951 /* 952 * We can't know for sure which types should be allocation cloneable on a particular 953 * platform but assume that at least totally trivial objects should be. 954 */ 955 ResolvedJavaType trivialCloneable = metaAccess.lookupJavaType(TrivialCloneable.class); 956 assertTrue(trivialCloneable.toString(), trivialCloneable.isCloneableWithAllocation()); 957 } 958 959 @Test 960 public void findMethodTest() { 961 try { 962 ResolvedJavaMethod findFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("()V")); 963 ResolvedJavaMethod expectedFoo = metaAccess.lookupJavaMethod(D.class.getDeclaredMethod("foo")); 964 assertEquals(expectedFoo, findFoo); 965 966 ResolvedJavaMethod wrongReturnTypeFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("()I")); 967 assertNull(wrongReturnTypeFoo); 968 969 ResolvedJavaMethod wrongArgumentsFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("(I)V")); 970 assertNull(wrongArgumentsFoo); 971 972 ResolvedJavaMethod wrongNameFoo = metaAccess.lookupJavaType(D.class).findMethod("bar", metaAccess.parseMethodDescriptor("()V")); 973 assertNull(wrongNameFoo); 974 975 ResolvedJavaMethod wrongClassFoo = metaAccess.lookupJavaType(SubD.class).findMethod("foo", metaAccess.parseMethodDescriptor("()V")); 976 assertNull(wrongClassFoo); 977 } catch (NoSuchMethodException | SecurityException e) { 978 throw new RuntimeException(e); 979 } 980 } 981 982 private Method findTestMethod(Method apiMethod) { 983 String testName = apiMethod.getName() + "Test"; 984 for (Method m : getClass().getDeclaredMethods()) { 985 if (m.getName().equals(testName) && m.getAnnotation(Test.class) != null) { 986 return m; 987 } 988 } 989 return null; 990 } 991 992 // @formatter:off 993 private static final String[] untestedApiMethods = { 994 "initialize", 995 "isPrimitive", 996 "newArray", 997 "getDeclaredConstructors", 998 "isInitialized", 999 "isLinked", 1000 "getJavaClass", 1001 "getObjectHub", 1002 "hasFinalizableSubclass", 1003 "hasFinalizer", 1004 "getSourceFileName", 1005 "isLocal", 1006 "isJavaLangObject", 1007 "isMember", 1008 "getElementalType", 1009 "getEnclosingType", 1010 "lookupType", 1011 "resolveField", 1012 "$jacocoInit" 1013 }; 1014 // @formatter:on 1015 1016 /** 1017 * Ensures that any new methods added to {@link ResolvedJavaMethod} either have a test written 1018 * for them or are added to {@link #untestedApiMethods}. 1019 */ 1020 @Test 1021 public void testCoverage() { 1022 Set<String> known = new HashSet<>(Arrays.asList(untestedApiMethods)); 1023 for (Method m : ResolvedJavaType.class.getDeclaredMethods()) { 1024 if (findTestMethod(m) == null) { 1025 assertTrue("test missing for " + m, known.contains(m.getName())); 1026 } else { 1027 assertFalse("test should be removed from untestedApiMethods" + m, known.contains(m.getName())); 1028 } 1029 } 1030 } 1031 1032 private static boolean isSignaturePolymorphic(ResolvedJavaMethod method) { 1033 return method.getAnnotation(SIGNATURE_POLYMORPHIC_CLASS) != null; 1034 } 1035 }