001/* 002 * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package jdk.internal.jvmci.runtime.test; 024 025import static java.lang.reflect.Modifier.*; 026import static org.junit.Assert.*; 027 028import java.lang.annotation.*; 029import java.lang.reflect.*; 030import java.net.*; 031import java.util.*; 032 033import jdk.internal.jvmci.common.*; 034import jdk.internal.jvmci.meta.*; 035import jdk.internal.jvmci.meta.Assumptions.*; 036 037import org.junit.*; 038 039import sun.reflect.ConstantPool; 040 041/** 042 * Tests for {@link ResolvedJavaType}. 043 */ 044public class TestResolvedJavaType extends TypeUniverse { 045 046 public TestResolvedJavaType() { 047 } 048 049 @Test 050 public void findInstanceFieldWithOffsetTest() { 051 for (Class<?> c : classes) { 052 ResolvedJavaType type = metaAccess.lookupJavaType(c); 053 Set<Field> reflectionFields = getInstanceFields(c, true); 054 for (Field f : reflectionFields) { 055 ResolvedJavaField rf = lookupField(type.getInstanceFields(true), f); 056 assertNotNull(rf); 057 long offset = isStatic(f.getModifiers()) ? unsafe.staticFieldOffset(f) : unsafe.objectFieldOffset(f); 058 ResolvedJavaField result = type.findInstanceFieldWithOffset(offset, rf.getKind()); 059 assertNotNull(result); 060 assertTrue(fieldsEqual(f, result)); 061 } 062 } 063 } 064 065 @Test 066 public void isInterfaceTest() { 067 for (Class<?> c : classes) { 068 ResolvedJavaType type = metaAccess.lookupJavaType(c); 069 boolean expected = c.isInterface(); 070 boolean actual = type.isInterface(); 071 assertEquals(expected, actual); 072 } 073 } 074 075 @Test 076 public void isInstanceClassTest() { 077 for (Class<?> c : classes) { 078 ResolvedJavaType type = metaAccess.lookupJavaType(c); 079 boolean expected = !c.isArray() && !c.isPrimitive() && !c.isInterface(); 080 boolean actual = type.isInstanceClass(); 081 assertEquals(expected, actual); 082 } 083 } 084 085 @Test 086 public void isArrayTest() { 087 for (Class<?> c : classes) { 088 ResolvedJavaType type = metaAccess.lookupJavaType(c); 089 boolean expected = c.isArray(); 090 boolean actual = type.isArray(); 091 assertEquals(expected, actual); 092 } 093 } 094 095 @Test 096 public void getModifiersTest() { 097 for (Class<?> c : classes) { 098 ResolvedJavaType type = metaAccess.lookupJavaType(c); 099 int expected = c.getModifiers() & ModifiersProvider.jvmClassModifiers(); 100 int actual = type.getModifiers() & ModifiersProvider.jvmClassModifiers(); 101 Class<?> elementalType = c; 102 while (elementalType.isArray()) { 103 elementalType = elementalType.getComponentType(); 104 } 105 if (elementalType.isMemberClass()) { 106 // member class get their modifiers from the inner-class attribute in the JVM and 107 // from the classfile header in jvmci 108 expected &= ~(Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED); 109 actual &= ~(Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED); 110 } 111 assertEquals(String.format("%s: 0x%x != 0x%x", type, expected, actual), expected, actual); 112 } 113 } 114 115 @Test 116 public void isAssignableFromTest() { 117 Class<?>[] all = classes.toArray(new Class[classes.size()]); 118 for (int i = 0; i < all.length; i++) { 119 Class<?> c1 = all[i]; 120 for (int j = i; j < all.length; j++) { 121 Class<?> c2 = all[j]; 122 ResolvedJavaType t1 = metaAccess.lookupJavaType(c1); 123 ResolvedJavaType t2 = metaAccess.lookupJavaType(c2); 124 boolean expected = c1.isAssignableFrom(c2); 125 boolean actual = t1.isAssignableFrom(t2); 126 assertEquals(expected, actual); 127 if (expected && t1 != t2) { 128 assertFalse(t2.isAssignableFrom(t1)); 129 } 130 } 131 } 132 } 133 134 @Test 135 public void isInstanceTest() { 136 for (ConstantValue cv : constants()) { 137 JavaConstant c = cv.value; 138 if (c.getKind() == Kind.Object && !c.isNull()) { 139 ResolvedJavaType cType = metaAccess.lookupJavaType(c); 140 for (ResolvedJavaType t : javaTypes) { 141 if (t.isAssignableFrom(cType)) { 142 assertTrue(t.isInstance(c)); 143 } else { 144 assertFalse(t.isInstance(c)); 145 } 146 } 147 } 148 } 149 } 150 151 private static Class<?> asExactClass(Class<?> c) { 152 if (c.isArray()) { 153 if (asExactClass(c.getComponentType()) != null) { 154 return c; 155 } 156 } else { 157 if (c.isPrimitive() || Modifier.isFinal(c.getModifiers())) { 158 return c; 159 } 160 } 161 return null; 162 } 163 164 @Test 165 public void asExactTypeTest() { 166 for (Class<?> c : classes) { 167 ResolvedJavaType type = metaAccess.lookupJavaType(c); 168 ResolvedJavaType exactType = type.asExactType(); 169 Class<?> expected = asExactClass(c); 170 if (expected == null) { 171 assertTrue("exact(" + c.getName() + ") != null", exactType == null); 172 } else { 173 assertNotNull(exactType); 174 assertTrue(exactType.equals(metaAccess.lookupJavaType(expected))); 175 } 176 } 177 } 178 179 @Test 180 public void getSuperclassTest() { 181 for (Class<?> c : classes) { 182 ResolvedJavaType type = metaAccess.lookupJavaType(c); 183 Class<?> expected = c.getSuperclass(); 184 ResolvedJavaType actual = type.getSuperclass(); 185 if (expected == null) { 186 assertTrue(actual == null); 187 } else { 188 assertNotNull(actual); 189 assertTrue(actual.equals(metaAccess.lookupJavaType(expected))); 190 } 191 } 192 } 193 194 @Test 195 public void getInterfacesTest() { 196 for (Class<?> c : classes) { 197 ResolvedJavaType type = metaAccess.lookupJavaType(c); 198 Class<?>[] expected = c.getInterfaces(); 199 ResolvedJavaType[] actual = type.getInterfaces(); 200 assertEquals(expected.length, actual.length); 201 for (int i = 0; i < expected.length; i++) { 202 assertTrue(actual[i].equals(metaAccess.lookupJavaType(expected[i]))); 203 } 204 } 205 } 206 207 public Class<?> getSupertype(Class<?> c) { 208 assert !c.isPrimitive(); 209 if (c.isArray()) { 210 Class<?> componentType = c.getComponentType(); 211 if (componentType.isPrimitive() || componentType == Object.class) { 212 return Object.class; 213 } 214 return getArrayClass(getSupertype(componentType)); 215 } 216 if (c.isInterface()) { 217 return Object.class; 218 } 219 return c.getSuperclass(); 220 } 221 222 public Class<?> findLeastCommonAncestor(Class<?> c1Initial, Class<?> c2Initial) { 223 if (c1Initial.isPrimitive() || c2Initial.isPrimitive()) { 224 return null; 225 } else { 226 Class<?> c1 = c1Initial; 227 Class<?> c2 = c2Initial; 228 while (true) { 229 if (c1.isAssignableFrom(c2)) { 230 return c1; 231 } 232 if (c2.isAssignableFrom(c1)) { 233 return c2; 234 } 235 c1 = getSupertype(c1); 236 c2 = getSupertype(c2); 237 } 238 } 239 } 240 241 @Test 242 public void findLeastCommonAncestorTest() { 243 Class<?>[] all = classes.toArray(new Class[classes.size()]); 244 for (int i = 0; i < all.length; i++) { 245 Class<?> c1 = all[i]; 246 for (int j = i; j < all.length; j++) { 247 Class<?> c2 = all[j]; 248 ResolvedJavaType t1 = metaAccess.lookupJavaType(c1); 249 ResolvedJavaType t2 = metaAccess.lookupJavaType(c2); 250 Class<?> expected = findLeastCommonAncestor(c1, c2); 251 ResolvedJavaType actual = t1.findLeastCommonAncestor(t2); 252 if (expected == null) { 253 assertTrue(actual == null); 254 } else { 255 assertNotNull(actual); 256 assertTrue(actual.equals(metaAccess.lookupJavaType(expected))); 257 } 258 } 259 } 260 } 261 262 private static class Base { 263 } 264 265 abstract static class Abstract1 extends Base { 266 } 267 268 interface Interface1 { 269 } 270 271 static class Concrete1 extends Abstract1 { 272 } 273 274 static class Concrete2 extends Abstract1 implements Interface1 { 275 } 276 277 static class Concrete3 extends Concrete2 { 278 } 279 280 static final class Final1 extends Abstract1 { 281 } 282 283 abstract static class Abstract4 extends Concrete3 { 284 } 285 286 void checkConcreteSubtype(ResolvedJavaType type, ResolvedJavaType expected) { 287 AssumptionResult<ResolvedJavaType> leafConcreteSubtype = type.findLeafConcreteSubtype(); 288 if (leafConcreteSubtype == null) { 289 // findLeafConcreteSubtype() is conservative 290 } else { 291 if (expected == null) { 292 assertNull(leafConcreteSubtype); 293 } else { 294 assertTrue(leafConcreteSubtype.getResult().equals(expected)); 295 } 296 } 297 298 if (!type.isArray()) { 299 ResolvedJavaType arrayType = type.getArrayClass(); 300 AssumptionResult<ResolvedJavaType> arraySubtype = arrayType.findLeafConcreteSubtype(); 301 if (arraySubtype != null) { 302 assertEquals(arraySubtype.getResult(), arrayType); 303 } else { 304 // findLeafConcreteSubtype() method is conservative 305 } 306 } 307 } 308 309 @Test 310 public void findLeafConcreteSubtypeTest() { 311 ResolvedJavaType base = metaAccess.lookupJavaType(Base.class); 312 checkConcreteSubtype(base, base); 313 314 ResolvedJavaType a1 = metaAccess.lookupJavaType(Abstract1.class); 315 ResolvedJavaType c1 = metaAccess.lookupJavaType(Concrete1.class); 316 317 checkConcreteSubtype(base, null); 318 checkConcreteSubtype(a1, c1); 319 checkConcreteSubtype(c1, c1); 320 321 ResolvedJavaType i1 = metaAccess.lookupJavaType(Interface1.class); 322 ResolvedJavaType c2 = metaAccess.lookupJavaType(Concrete2.class); 323 324 checkConcreteSubtype(base, null); 325 checkConcreteSubtype(a1, null); 326 checkConcreteSubtype(c1, c1); 327 checkConcreteSubtype(i1, c2); 328 checkConcreteSubtype(c2, c2); 329 330 ResolvedJavaType c3 = metaAccess.lookupJavaType(Concrete3.class); 331 checkConcreteSubtype(c2, null); 332 checkConcreteSubtype(c3, c3); 333 334 ResolvedJavaType a4 = metaAccess.lookupJavaType(Abstract4.class); 335 checkConcreteSubtype(c3, null); 336 checkConcreteSubtype(a4, null); 337 338 ResolvedJavaType a1a = metaAccess.lookupJavaType(Abstract1[].class); 339 checkConcreteSubtype(a1a, null); 340 ResolvedJavaType c1a = metaAccess.lookupJavaType(Concrete1[].class); 341 checkConcreteSubtype(c1a, null); 342 ResolvedJavaType f1a = metaAccess.lookupJavaType(Final1[].class); 343 checkConcreteSubtype(f1a, f1a); 344 345 ResolvedJavaType obja = metaAccess.lookupJavaType(Object[].class); 346 checkConcreteSubtype(obja, null); 347 348 ResolvedJavaType inta = metaAccess.lookupJavaType(int[].class); 349 checkConcreteSubtype(inta, inta); 350 } 351 352 interface NoImplementor { 353 } 354 355 interface SingleImplementorInterface { 356 } 357 358 static class SingleConcreteImplementor implements SingleImplementorInterface { 359 } 360 361 interface SingleAbstractImplementorInterface { 362 } 363 364 abstract static class SingleAbstractImplementor implements SingleAbstractImplementorInterface { 365 } 366 367 interface MultiImplementorInterface { 368 } 369 370 static class ConcreteImplementor1 implements MultiImplementorInterface { 371 } 372 373 static class ConcreteImplementor2 implements MultiImplementorInterface { 374 } 375 376 interface MultipleAbstractImplementorInterface { 377 } 378 379 abstract static class MultiAbstractImplementor1 implements MultipleAbstractImplementorInterface { 380 } 381 382 abstract static class MultiAbstractImplementor2 implements MultipleAbstractImplementorInterface { 383 } 384 385 interface SingleAbstractImplementorInterface2 { 386 } 387 388 interface ExtendedSingleImplementorInterface { 389 } 390 391 abstract static class SingleAbstractImplementor2 implements SingleAbstractImplementorInterface2 { 392 } 393 394 static class ConcreteTransitiveImplementor1 extends SingleAbstractImplementor2 implements ExtendedSingleImplementorInterface { 395 } 396 397 static class ConcreteTransitiveImplementor2 extends SingleAbstractImplementor2 implements ExtendedSingleImplementorInterface { 398 } 399 400 @Test 401 public void getSingleImplementorTest() { 402 ResolvedJavaType iNi = metaAccess.lookupJavaType(NoImplementor.class); 403 assertNull(iNi.getSingleImplementor()); 404 405 ResolvedJavaType iSi = metaAccess.lookupJavaType(SingleImplementorInterface.class); 406 ResolvedJavaType cSi = metaAccess.lookupJavaType(SingleConcreteImplementor.class); 407 assertEquals(cSi, iSi.getSingleImplementor()); 408 409 ResolvedJavaType iSai = metaAccess.lookupJavaType(SingleAbstractImplementorInterface.class); 410 ResolvedJavaType aSai = metaAccess.lookupJavaType(SingleAbstractImplementor.class); 411 assertEquals(aSai, iSai.getSingleImplementor()); 412 413 ResolvedJavaType iMi = metaAccess.lookupJavaType(MultiImplementorInterface.class); 414 metaAccess.lookupJavaType(ConcreteImplementor1.class); 415 metaAccess.lookupJavaType(ConcreteImplementor2.class); 416 assertEquals(iMi, iMi.getSingleImplementor()); 417 418 ResolvedJavaType iMai = metaAccess.lookupJavaType(MultipleAbstractImplementorInterface.class); 419 metaAccess.lookupJavaType(MultiAbstractImplementor1.class); 420 metaAccess.lookupJavaType(MultiAbstractImplementor2.class); 421 assertEquals(iMai, iMai.getSingleImplementor()); 422 423 ResolvedJavaType iSai2 = metaAccess.lookupJavaType(SingleAbstractImplementorInterface2.class); 424 ResolvedJavaType aSai2 = metaAccess.lookupJavaType(SingleAbstractImplementor2.class); 425 metaAccess.lookupJavaType(ConcreteTransitiveImplementor1.class); 426 metaAccess.lookupJavaType(ConcreteTransitiveImplementor2.class); 427 assertEquals(aSai2, iSai2.getSingleImplementor()); 428 } 429 430 @Test(expected = JVMCIError.class) 431 public void getSingleImplementorTestClassReceiver() { 432 ResolvedJavaType base = metaAccess.lookupJavaType(Base.class); 433 base.getSingleImplementor(); 434 } 435 436 @Test(expected = JVMCIError.class) 437 public void getSingleImplementorTestPrimitiveReceiver() { 438 ResolvedJavaType primitive = metaAccess.lookupJavaType(int.class); 439 primitive.getSingleImplementor(); 440 } 441 442 @Test 443 public void getComponentTypeTest() { 444 for (Class<?> c : classes) { 445 ResolvedJavaType type = metaAccess.lookupJavaType(c); 446 Class<?> expected = c.getComponentType(); 447 ResolvedJavaType actual = type.getComponentType(); 448 if (expected == null) { 449 assertNull(actual); 450 } else { 451 assertTrue(actual.equals(metaAccess.lookupJavaType(expected))); 452 } 453 } 454 } 455 456 @Test 457 public void getArrayClassTest() { 458 for (Class<?> c : classes) { 459 if (c != void.class) { 460 ResolvedJavaType type = metaAccess.lookupJavaType(c); 461 Class<?> expected = getArrayClass(c); 462 ResolvedJavaType actual = type.getArrayClass(); 463 assertTrue(actual.equals(metaAccess.lookupJavaType(expected))); 464 } 465 } 466 } 467 468 static class Declarations { 469 470 final Method implementation; 471 final Set<Method> declarations; 472 473 public Declarations(Method impl) { 474 this.implementation = impl; 475 declarations = new HashSet<>(); 476 } 477 } 478 479 /** 480 * See <a href="http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.4.5">Method 481 * overriding</a>. 482 */ 483 static boolean isOverriderOf(Method impl, Method m) { 484 if (!isPrivate(m.getModifiers()) && !isFinal(m.getModifiers())) { 485 if (m.getName().equals(impl.getName())) { 486 if (m.getReturnType() == impl.getReturnType()) { 487 if (Arrays.equals(m.getParameterTypes(), impl.getParameterTypes())) { 488 if (isPublic(m.getModifiers()) || isProtected(m.getModifiers())) { 489 // m is public or protected 490 return isPublic(impl.getModifiers()) || isProtected(impl.getModifiers()); 491 } else { 492 // m is package-private 493 return impl.getDeclaringClass().getPackage() == m.getDeclaringClass().getPackage(); 494 } 495 } 496 } 497 } 498 } 499 return false; 500 } 501 502 static final Map<Class<?>, VTable> vtables = new HashMap<>(); 503 504 static class VTable { 505 506 final Map<NameAndSignature, Method> methods = new HashMap<>(); 507 } 508 509 static synchronized VTable getVTable(Class<?> c) { 510 VTable vtable = vtables.get(c); 511 if (vtable == null) { 512 vtable = new VTable(); 513 if (c != Object.class) { 514 VTable superVtable = getVTable(c.getSuperclass()); 515 vtable.methods.putAll(superVtable.methods); 516 } 517 for (Method m : c.getDeclaredMethods()) { 518 if (!isStatic(m.getModifiers()) && !isPrivate(m.getModifiers())) { 519 if (isAbstract(m.getModifiers())) { 520 // A subclass makes a concrete method in a superclass abstract 521 vtable.methods.remove(new NameAndSignature(m)); 522 } else { 523 vtable.methods.put(new NameAndSignature(m), m); 524 } 525 } 526 } 527 vtables.put(c, vtable); 528 } 529 return vtable; 530 } 531 532 static Set<Method> findDeclarations(Method impl, Class<?> c) { 533 Set<Method> declarations = new HashSet<>(); 534 NameAndSignature implSig = new NameAndSignature(impl); 535 if (c != null) { 536 for (Method m : c.getDeclaredMethods()) { 537 if (new NameAndSignature(m).equals(implSig)) { 538 declarations.add(m); 539 break; 540 } 541 } 542 if (!c.isInterface()) { 543 declarations.addAll(findDeclarations(impl, c.getSuperclass())); 544 } 545 for (Class<?> i : c.getInterfaces()) { 546 declarations.addAll(findDeclarations(impl, i)); 547 } 548 } 549 return declarations; 550 } 551 552 private static void checkResolveMethod(ResolvedJavaType type, ResolvedJavaType context, ResolvedJavaMethod decl, ResolvedJavaMethod expected) { 553 ResolvedJavaMethod impl = type.resolveConcreteMethod(decl, context); 554 assertEquals(expected, impl); 555 } 556 557 @Test 558 public void resolveMethodTest() { 559 ResolvedJavaType context = metaAccess.lookupJavaType(TestResolvedJavaType.class); 560 for (Class<?> c : classes) { 561 if (c.isInterface() || c.isPrimitive()) { 562 ResolvedJavaType type = metaAccess.lookupJavaType(c); 563 for (Method m : c.getDeclaredMethods()) { 564 if (JAVA_VERSION <= 1.7D || (!isStatic(m.getModifiers()) && !isPrivate(m.getModifiers()))) { 565 ResolvedJavaMethod resolved = metaAccess.lookupJavaMethod(m); 566 ResolvedJavaMethod impl = type.resolveMethod(resolved, context); 567 ResolvedJavaMethod expected = resolved.isDefault() || resolved.isAbstract() ? resolved : null; 568 assertEquals(m.toString(), expected, impl); 569 } else { 570 // As of JDK 8, interfaces can have static and private methods 571 } 572 } 573 } else { 574 ResolvedJavaType type = metaAccess.lookupJavaType(c); 575 VTable vtable = getVTable(c); 576 for (Method impl : vtable.methods.values()) { 577 Set<Method> decls = findDeclarations(impl, c); 578 for (Method decl : decls) { 579 ResolvedJavaMethod m = metaAccess.lookupJavaMethod(decl); 580 if (m.isPublic()) { 581 ResolvedJavaMethod i = metaAccess.lookupJavaMethod(impl); 582 checkResolveMethod(type, context, m, i); 583 } 584 } 585 } 586 } 587 } 588 } 589 590 @Test 591 public void resolveConcreteMethodTest() { 592 ResolvedJavaType context = metaAccess.lookupJavaType(TestResolvedJavaType.class); 593 for (Class<?> c : classes) { 594 if (c.isInterface() || c.isPrimitive()) { 595 ResolvedJavaType type = metaAccess.lookupJavaType(c); 596 for (Method m : c.getDeclaredMethods()) { 597 if (JAVA_VERSION <= 1.7D || (!isStatic(m.getModifiers()) && !isPrivate(m.getModifiers()))) { 598 ResolvedJavaMethod resolved = metaAccess.lookupJavaMethod(m); 599 ResolvedJavaMethod impl = type.resolveConcreteMethod(resolved, context); 600 ResolvedJavaMethod expected = resolved.isDefault() ? resolved : null; 601 assertEquals(m.toString(), expected, impl); 602 } else { 603 // As of JDK 8, interfaces can have static and private methods 604 } 605 } 606 } else { 607 ResolvedJavaType type = metaAccess.lookupJavaType(c); 608 VTable vtable = getVTable(c); 609 for (Method impl : vtable.methods.values()) { 610 Set<Method> decls = findDeclarations(impl, c); 611 for (Method decl : decls) { 612 ResolvedJavaMethod m = metaAccess.lookupJavaMethod(decl); 613 if (m.isPublic()) { 614 ResolvedJavaMethod i = metaAccess.lookupJavaMethod(impl); 615 checkResolveMethod(type, context, m, i); 616 } 617 } 618 } 619 for (Method m : c.getDeclaredMethods()) { 620 ResolvedJavaMethod impl = type.resolveConcreteMethod(metaAccess.lookupJavaMethod(m), context); 621 ResolvedJavaMethod expected = isAbstract(m.getModifiers()) ? null : impl; 622 assertEquals(type + " " + m.toString(), expected, impl); 623 } 624 } 625 } 626 } 627 628 @Test 629 public void findUniqueConcreteMethodTest() throws NoSuchMethodException { 630 ResolvedJavaMethod thisMethod = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("findUniqueConcreteMethodTest")); 631 ResolvedJavaMethod ucm = metaAccess.lookupJavaType(getClass()).findUniqueConcreteMethod(thisMethod).getResult(); 632 assertEquals(thisMethod, ucm); 633 } 634 635 public static Set<Field> getInstanceFields(Class<?> c, boolean includeSuperclasses) { 636 if (c.isArray() || c.isPrimitive() || c.isInterface()) { 637 return Collections.emptySet(); 638 } 639 Set<Field> result = new HashSet<>(); 640 for (Field f : c.getDeclaredFields()) { 641 if (!Modifier.isStatic(f.getModifiers())) { 642 result.add(f); 643 } 644 } 645 if (includeSuperclasses && c != Object.class) { 646 result.addAll(getInstanceFields(c.getSuperclass(), true)); 647 } 648 return result; 649 } 650 651 public static Set<Field> getStaticFields(Class<?> c) { 652 Set<Field> result = new HashSet<>(); 653 for (Field f : c.getDeclaredFields()) { 654 if (Modifier.isStatic(f.getModifiers())) { 655 result.add(f); 656 } 657 } 658 return result; 659 } 660 661 public boolean fieldsEqual(Field f, ResolvedJavaField rjf) { 662 return rjf.getDeclaringClass().equals(metaAccess.lookupJavaType(f.getDeclaringClass())) && rjf.getName().equals(f.getName()) && 663 rjf.getType().resolve(rjf.getDeclaringClass()).equals(metaAccess.lookupJavaType(f.getType())); 664 } 665 666 public ResolvedJavaField lookupField(ResolvedJavaField[] fields, Field key) { 667 for (ResolvedJavaField rf : fields) { 668 if (fieldsEqual(key, rf)) { 669 return rf; 670 } 671 } 672 return null; 673 } 674 675 public Field lookupField(Set<Field> fields, ResolvedJavaField key) { 676 for (Field f : fields) { 677 if (fieldsEqual(f, key)) { 678 return f; 679 } 680 } 681 return null; 682 } 683 684 private static boolean isHiddenFromReflection(ResolvedJavaField f) { 685 if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(Throwable.class)) && f.getName().equals("backtrace")) { 686 return true; 687 } 688 if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(ConstantPool.class)) && f.getName().equals("constantPoolOop")) { 689 return true; 690 } 691 if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(Class.class)) && f.getName().equals("classLoader")) { 692 return true; 693 } 694 return false; 695 } 696 697 @Test 698 public void getInstanceFieldsTest() { 699 for (Class<?> c : classes) { 700 ResolvedJavaType type = metaAccess.lookupJavaType(c); 701 for (boolean includeSuperclasses : new boolean[]{true, false}) { 702 Set<Field> expected = getInstanceFields(c, includeSuperclasses); 703 ResolvedJavaField[] actual = type.getInstanceFields(includeSuperclasses); 704 for (Field f : expected) { 705 assertNotNull(lookupField(actual, f)); 706 } 707 for (ResolvedJavaField rf : actual) { 708 if (!isHiddenFromReflection(rf)) { 709 assertEquals(rf.toString(), lookupField(expected, rf) != null, !rf.isInternal()); 710 } 711 } 712 713 // Test stability of getInstanceFields 714 ResolvedJavaField[] actual2 = type.getInstanceFields(includeSuperclasses); 715 assertArrayEquals(actual, actual2); 716 } 717 } 718 } 719 720 @Test 721 public void getStaticFieldsTest() { 722 for (Class<?> c : classes) { 723 ResolvedJavaType type = metaAccess.lookupJavaType(c); 724 Set<Field> expected = getStaticFields(c); 725 ResolvedJavaField[] actual = type.getStaticFields(); 726 for (Field f : expected) { 727 assertNotNull(lookupField(actual, f)); 728 } 729 for (ResolvedJavaField rf : actual) { 730 if (!isHiddenFromReflection(rf)) { 731 assertEquals(lookupField(expected, rf) != null, !rf.isInternal()); 732 } 733 } 734 735 // Test stability of getStaticFields 736 ResolvedJavaField[] actual2 = type.getStaticFields(); 737 assertArrayEquals(actual, actual2); 738 } 739 } 740 741 @Test 742 public void getDeclaredMethodsTest() { 743 for (Class<?> c : classes) { 744 ResolvedJavaType type = metaAccess.lookupJavaType(c); 745 Method[] raw = c.getDeclaredMethods(); 746 Set<ResolvedJavaMethod> expected = new HashSet<>(); 747 for (Method m : raw) { 748 ResolvedJavaMethod resolvedMethod = metaAccess.lookupJavaMethod(m); 749 assertNotNull(resolvedMethod); 750 expected.add(resolvedMethod); 751 } 752 Set<ResolvedJavaMethod> actual = new HashSet<>(Arrays.asList(type.getDeclaredMethods())); 753 assertEquals(expected, actual); 754 } 755 } 756 757 static class A { 758 static String name = "foo"; 759 } 760 761 static class B extends A { 762 } 763 764 static class C { 765 } 766 767 static class D { 768 void foo() { 769 // use of assertions causes the class to have a <clinit> 770 assert getClass() != null; 771 } 772 } 773 774 static class SubD extends D { 775 776 } 777 778 @Test 779 public void getClassInitializerTest() { 780 assertNotNull(metaAccess.lookupJavaType(A.class).getClassInitializer()); 781 assertNotNull(metaAccess.lookupJavaType(D.class).getClassInitializer()); 782 assertNull(metaAccess.lookupJavaType(B.class).getClassInitializer()); 783 assertNull(metaAccess.lookupJavaType(C.class).getClassInitializer()); 784 assertNull(metaAccess.lookupJavaType(int.class).getClassInitializer()); 785 assertNull(metaAccess.lookupJavaType(void.class).getClassInitializer()); 786 } 787 788 @Test 789 public void getAnnotationTest() { 790 for (Class<?> c : classes) { 791 ResolvedJavaType type = metaAccess.lookupJavaType(c); 792 for (Annotation a : c.getAnnotations()) { 793 assertEquals(a, type.getAnnotation(a.annotationType())); 794 } 795 } 796 } 797 798 @Test 799 public void memberClassesTest() { 800 for (Class<?> c : classes) { 801 ResolvedJavaType type = metaAccess.lookupJavaType(c); 802 assertEquals(c.isLocalClass(), type.isLocal()); 803 assertEquals(c.isMemberClass(), type.isMember()); 804 Class<?> enclc = c.getEnclosingClass(); 805 ResolvedJavaType enclt = type.getEnclosingType(); 806 assertFalse(enclc == null ^ enclt == null); 807 if (enclc != null) { 808 assertEquals(enclt, metaAccess.lookupJavaType(enclc)); 809 } 810 } 811 } 812 813 @Test 814 public void classFilePathTest() { 815 for (Class<?> c : classes) { 816 ResolvedJavaType type = metaAccess.lookupJavaType(c); 817 URL path = type.getClassFilePath(); 818 if (type.isPrimitive() || type.isArray()) { 819 assertEquals(null, path); 820 } else { 821 assertNotNull(path); 822 String pathString = path.getPath(); 823 if (type.isLocal() || type.isMember()) { 824 assertTrue(pathString.indexOf('$') > 0); 825 } 826 } 827 } 828 } 829 830 @Test 831 public void isTrustedInterfaceTypeTest() { 832 for (Class<?> c : classes) { 833 ResolvedJavaType type = metaAccess.lookupJavaType(c); 834 if (TrustedInterface.class.isAssignableFrom(c)) { 835 assertTrue(type.isTrustedInterfaceType()); 836 } 837 } 838 } 839 840 @Test 841 public void isLeafTest() { 842 for (Class<?> c : classes) { 843 ResolvedJavaType type = metaAccess.lookupJavaType(c); 844 ResolvedJavaType arrayType = c != void.class ? metaAccess.lookupJavaType(getArrayClass(c)) : null; 845 if (c.isPrimitive()) { 846 assertTrue(type.isLeaf()); 847 assertTrue(arrayType == null || arrayType.isLeaf()); 848 } else { 849 assertTrue(c.toString(), type.isLeaf() == arrayType.isLeaf()); 850 if (!c.isArray()) { 851 assertTrue(c.toString(), type.isLeaf() == Modifier.isFinal(c.getModifiers())); 852 } 853 } 854 } 855 } 856 857 @Test 858 public void findMethodTest() { 859 try { 860 ResolvedJavaMethod findFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("()V")); 861 ResolvedJavaMethod expectedFoo = metaAccess.lookupJavaMethod(D.class.getDeclaredMethod("foo")); 862 assertEquals(expectedFoo, findFoo); 863 864 ResolvedJavaMethod wrongReturnTypeFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("()I")); 865 assertNull(wrongReturnTypeFoo); 866 867 ResolvedJavaMethod wrongArgumentsFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("(I)V")); 868 assertNull(wrongArgumentsFoo); 869 870 ResolvedJavaMethod wrongNameFoo = metaAccess.lookupJavaType(D.class).findMethod("bar", metaAccess.parseMethodDescriptor("()V")); 871 assertNull(wrongNameFoo); 872 873 ResolvedJavaMethod wrongClassFoo = metaAccess.lookupJavaType(SubD.class).findMethod("foo", metaAccess.parseMethodDescriptor("()V")); 874 assertNull(wrongClassFoo); 875 } catch (NoSuchMethodException | SecurityException e) { 876 throw new RuntimeException(e); 877 } 878 } 879 880 private Method findTestMethod(Method apiMethod) { 881 String testName = apiMethod.getName() + "Test"; 882 for (Method m : getClass().getDeclaredMethods()) { 883 if (m.getName().equals(testName) && m.getAnnotation(Test.class) != null) { 884 return m; 885 } 886 } 887 return null; 888 } 889 890 // @formatter:off 891 private static final String[] untestedApiMethods = { 892 "initialize", 893 "isPrimitive", 894 "newArray", 895 "getDeclaredConstructors", 896 "isInitialized", 897 "isLinked", 898 "getJavaClass", 899 "getObjectHub", 900 "hasFinalizableSubclass", 901 "hasFinalizer", 902 "getSourceFileName", 903 "getClassFilePath", 904 "isLocal", 905 "isJavaLangObject", 906 "isMember", 907 "getElementalType", 908 "getEnclosingType", 909 "$jacocoInit" 910 }; 911 // @formatter:on 912 913 /** 914 * Ensures that any new methods added to {@link ResolvedJavaMethod} either have a test written 915 * for them or are added to {@link #untestedApiMethods}. 916 */ 917 @Test 918 public void testCoverage() { 919 Set<String> known = new HashSet<>(Arrays.asList(untestedApiMethods)); 920 for (Method m : ResolvedJavaType.class.getDeclaredMethods()) { 921 if (findTestMethod(m) == null) { 922 assertTrue("test missing for " + m, known.contains(m.getName())); 923 } else { 924 assertFalse("test should be removed from untestedApiMethods" + m, known.contains(m.getName())); 925 } 926 } 927 } 928}