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