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