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