1 /*
2  * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 
25 package org.graalvm.compiler.replacements.test;
26 
27 import java.util.Arrays;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.TreeMap;
32 
33 import org.graalvm.compiler.debug.DebugContext;
34 import org.graalvm.compiler.nodes.IfNode;
35 import org.graalvm.compiler.nodes.ReturnNode;
36 import org.graalvm.compiler.nodes.StructuredGraph;
37 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
38 import org.graalvm.compiler.nodes.java.InstanceOfNode;
39 import org.graalvm.compiler.phases.common.AbstractInliningPhase;
40 import org.junit.Test;
41 
42 import jdk.vm.ci.code.site.Call;
43 import jdk.vm.ci.code.site.Mark;
44 import jdk.vm.ci.code.site.Site;
45 import jdk.vm.ci.meta.JavaTypeProfile;
46 
47 /**
48  * Tests the implementation of instanceof, allowing profiling information to be manually specified.
49  */
50 public class InstanceOfTest extends TypeCheckTest {
51 
InstanceOfTest()52     public InstanceOfTest() {
53         createSuites(getInitialOptions()).getHighTier().findPhase(AbstractInliningPhase.class).remove();
54     }
55 
56     @Override
replaceProfile(StructuredGraph graph, JavaTypeProfile profile)57     protected void replaceProfile(StructuredGraph graph, JavaTypeProfile profile) {
58         InstanceOfNode ion = graph.getNodes().filter(InstanceOfNode.class).first();
59         if (ion != null) {
60             ion.setProfile(profile, graph.start());
61         }
62     }
63 
64     @Test
test1()65     public void test1() {
66         test("isString", profile(), "object");
67         test("isString", profile(String.class), "object");
68 
69         test("isString", profile(), Object.class);
70         test("isString", profile(String.class), Object.class);
71     }
72 
73     @Test
test2()74     public void test2() {
75         test("isStringInt", profile(), "object");
76         test("isStringInt", profile(String.class), "object");
77 
78         test("isStringInt", profile(), Object.class);
79         test("isStringInt", profile(String.class), Object.class);
80     }
81 
82     @Test
test201()83     public void test201() {
84         test("isStringIntComplex", profile(), "object");
85         test("isStringIntComplex", profile(String.class), "object");
86 
87         test("isStringIntComplex", profile(), Object.class);
88         test("isStringIntComplex", profile(String.class), Object.class);
89     }
90 
91     @Test
test3()92     public void test3() {
93         Throwable throwable = new Exception();
94         test("isThrowable", profile(), throwable);
95         test("isThrowable", profile(Throwable.class), throwable);
96         test("isThrowable", profile(Exception.class, Error.class), throwable);
97 
98         test("isThrowable", profile(), Object.class);
99         test("isThrowable", profile(Throwable.class), Object.class);
100         test("isThrowable", profile(Exception.class, Error.class), Object.class);
101     }
102 
103     @Test
test301()104     public void test301() {
105         onlyFirstIsException(new Exception(), new Error());
106         test("onlyFirstIsException", profile(), new Exception(), new Error());
107         test("onlyFirstIsException", profile(), new Error(), new Exception());
108         test("onlyFirstIsException", profile(), new Exception(), new Exception());
109         test("onlyFirstIsException", profile(), new Error(), new Error());
110     }
111 
112     @Test
test4()113     public void test4() {
114         Throwable throwable = new Exception();
115         test("isThrowableInt", profile(), throwable);
116         test("isThrowableInt", profile(Throwable.class), throwable);
117         test("isThrowableInt", profile(Exception.class, Error.class), throwable);
118 
119         test("isThrowableInt", profile(), Object.class);
120         test("isThrowableInt", profile(Throwable.class), Object.class);
121         test("isThrowableInt", profile(Exception.class, Error.class), Object.class);
122     }
123 
124     @Test
test5()125     public void test5() {
126         Map<?, ?> map = new HashMap<>();
127         test("isMap", profile(), map);
128         test("isMap", profile(HashMap.class), map);
129         test("isMap", profile(TreeMap.class, HashMap.class), map);
130 
131         test("isMap", profile(), Object.class);
132         test("isMap", profile(HashMap.class), Object.class);
133         test("isMap", profile(TreeMap.class, HashMap.class), Object.class);
134         test("isMap", profile(String.class, HashMap.class), Object.class);
135     }
136 
137     @Test
test6()138     public void test6() {
139         Map<?, ?> map = new HashMap<>();
140         test("isMapInt", profile(), map);
141         test("isMapInt", profile(HashMap.class), map);
142         test("isMapInt", profile(TreeMap.class, HashMap.class), map);
143 
144         test("isMapInt", profile(), Object.class);
145         test("isMapInt", profile(HashMap.class), Object.class);
146         test("isMapInt", profile(TreeMap.class, HashMap.class), Object.class);
147     }
148 
149     @Test
test7()150     public void test7() {
151         Object o = new Depth13();
152         test("isDepth12", profile(), o);
153         test("isDepth12", profile(Depth13.class), o);
154         test("isDepth12", profile(Depth13.class, Depth14.class), o);
155 
156         o = "not a depth";
157         test("isDepth12", profile(), o);
158         test("isDepth12", profile(Depth13.class), o);
159         test("isDepth12", profile(Depth13.class, Depth14.class), o);
160         test("isDepth12", profile(String.class, HashMap.class), o);
161     }
162 
163     @Test
test8()164     public void test8() {
165         Object o = new Depth13();
166         test("isDepth12Int", profile(), o);
167         test("isDepth12Int", profile(Depth13.class), o);
168         test("isDepth12Int", profile(Depth13.class, Depth14.class), o);
169 
170         o = "not a depth";
171         test("isDepth12Int", profile(), o);
172         test("isDepth12Int", profile(Depth13.class), o);
173         test("isDepth12Int", profile(Depth13.class, Depth14.class), o);
174     }
175 
isString(Object o)176     public static boolean isString(Object o) {
177         return o instanceof String;
178     }
179 
isStringInt(Object o)180     public static int isStringInt(Object o) {
181         if (o instanceof String) {
182             return id(1);
183         }
184         return id(0);
185     }
186 
isStringIntComplex(Object o)187     public static int isStringIntComplex(Object o) {
188         if (o instanceof String || o instanceof Integer) {
189             return id(o instanceof String ? 1 : 0);
190         }
191         return id(0);
192     }
193 
id(int value)194     public static int id(int value) {
195         return value;
196     }
197 
isThrowable(Object o)198     public static boolean isThrowable(Object o) {
199         return ((Throwable) o) instanceof Exception;
200     }
201 
onlyFirstIsException(Throwable t1, Throwable t2)202     public static int onlyFirstIsException(Throwable t1, Throwable t2) {
203         if (t1 instanceof Exception ^ t2 instanceof Exception) {
204             return t1 instanceof Exception ? 1 : -1;
205         }
206         return -1;
207     }
208 
isThrowableInt(Object o)209     public static int isThrowableInt(Object o) {
210         int result = o instanceof Throwable ? 4 : 5;
211         if (o instanceof Throwable) {
212             return id(4);
213         }
214         return result;
215     }
216 
isMap(Object o)217     public static boolean isMap(Object o) {
218         return o instanceof Map;
219     }
220 
isMapInt(Object o)221     public static int isMapInt(Object o) {
222         if (o instanceof Map) {
223             return id(1);
224         }
225         return id(0);
226     }
227 
isDepth12(Object o)228     public static boolean isDepth12(Object o) {
229         return o instanceof Depth12;
230     }
231 
isDepth12Int(Object o)232     public static int isDepth12Int(Object o) {
233         if (o instanceof Depth12) {
234             return id(0);
235         }
236         return id(0);
237     }
238 
239     abstract static class MySite {
240 
241         final int offset;
242 
MySite(int offset)243         MySite(int offset) {
244             this.offset = offset;
245         }
246     }
247 
248     static class MyMark extends MySite {
249 
MyMark(int offset)250         MyMark(int offset) {
251             super(offset);
252         }
253     }
254 
255     abstract static class MySafepoint extends MySite {
256 
MySafepoint(int offset)257         MySafepoint(int offset) {
258             super(offset);
259         }
260     }
261 
262     static class MyCall extends MySafepoint {
263 
MyCall(int offset)264         MyCall(int offset) {
265             super(offset);
266         }
267     }
268 
269     @Test
test9()270     public void test9() {
271         MyCall callAt63 = new MyCall(63);
272         MyMark markAt63 = new MyMark(63);
273         test("compareMySites", callAt63, callAt63);
274         test("compareMySites", callAt63, markAt63);
275         test("compareMySites", markAt63, callAt63);
276         test("compareMySites", markAt63, markAt63);
277     }
278 
compareMySites(MySite s1, MySite s2)279     public static int compareMySites(MySite s1, MySite s2) {
280         if (s1.offset == s2.offset && (s1 instanceof MyMark ^ s2 instanceof MyMark)) {
281             return s1 instanceof MyMark ? -1 : 1;
282         }
283         return s1.offset - s2.offset;
284     }
285 
286     @Test
test10()287     public void test10() {
288         Call callAt63 = new Call(null, 63, 5, true, null);
289         Mark markAt63 = new Mark(63, "1");
290         test("compareSites", callAt63, callAt63);
291         test("compareSites", callAt63, markAt63);
292         test("compareSites", markAt63, callAt63);
293         test("compareSites", markAt63, markAt63);
294     }
295 
compareSites(Site s1, Site s2)296     public static int compareSites(Site s1, Site s2) {
297         if (s1.pcOffset == s2.pcOffset && (s1 instanceof Mark ^ s2 instanceof Mark)) {
298             return s1 instanceof Mark ? -1 : 1;
299         }
300         return s1.pcOffset - s2.pcOffset;
301     }
302 
303     /**
304      * This test exists to show the kind of pattern that is be optimizable by
305      * {@code removeIntermediateMaterialization()} in {@link IfNode}.
306      * <p>
307      * The test exists in this source file as the transformation was originally motivated by the
308      * need to remove use of special JumpNodes in the {@code InstanceOfSnippets}.
309      */
310     @Test
testRemoveIntermediateMaterialization()311     public void testRemoveIntermediateMaterialization() {
312         List<String> list = Arrays.asList("1", "2", "3", "4");
313         test("removeIntermediateMaterialization", profile(), list, "2", "yes", "no");
314         test("removeIntermediateMaterialization", profile(), list, null, "yes", "no");
315         test("removeIntermediateMaterialization", profile(), null, "2", "yes", "no");
316     }
317 
removeIntermediateMaterialization(List<Object> list, Object e, String a, String b)318     public static String removeIntermediateMaterialization(List<Object> list, Object e, String a, String b) {
319         boolean test;
320         if (list == null || e == null) {
321             test = false;
322         } else {
323             test = false;
324             for (Object i : list) {
325                 if (i.equals(e)) {
326                     test = true;
327                     break;
328                 }
329             }
330         }
331         if (test) {
332             return a;
333         }
334         return b;
335     }
336 
337     abstract static class A {
338     }
339 
340     static class B extends A {
341     }
342 
343     static class C extends B {
344     }
345 
346     abstract static class D extends C {
347     }
348 
isArrayOfA(Object o)349     public static boolean isArrayOfA(Object o) {
350         return o instanceof A[];
351     }
352 
isArrayOfB(Object o)353     public static boolean isArrayOfB(Object o) {
354         return o instanceof B[];
355     }
356 
isArrayOfC(Object o)357     public static boolean isArrayOfC(Object o) {
358         return o instanceof C[];
359     }
360 
isArrayOfD(Object o)361     public static boolean isArrayOfD(Object o) {
362         return o instanceof D[];
363     }
364 
365     @Test
testArray()366     public void testArray() {
367         Object aArray = new A[10];
368         test("isArrayOfA", aArray);
369 
370         Object bArray = new B[10];
371         test("isArrayOfA", aArray);
372         test("isArrayOfA", bArray);
373         test("isArrayOfB", aArray);
374         test("isArrayOfB", bArray);
375 
376         Object cArray = new C[10];
377         test("isArrayOfA", aArray);
378         test("isArrayOfA", bArray);
379         test("isArrayOfA", cArray);
380         test("isArrayOfB", aArray);
381         test("isArrayOfB", bArray);
382         test("isArrayOfB", cArray);
383         test("isArrayOfC", aArray);
384         test("isArrayOfC", bArray);
385         test("isArrayOfC", cArray);
386 
387         Object dArray = new D[10];
388         test("isArrayOfA", aArray);
389         test("isArrayOfA", bArray);
390         test("isArrayOfA", cArray);
391         test("isArrayOfA", dArray);
392         test("isArrayOfB", aArray);
393         test("isArrayOfB", bArray);
394         test("isArrayOfB", cArray);
395         test("isArrayOfB", dArray);
396         test("isArrayOfC", aArray);
397         test("isArrayOfC", bArray);
398         test("isArrayOfC", cArray);
399         test("isArrayOfC", dArray);
400         test("isArrayOfD", aArray);
401         test("isArrayOfD", bArray);
402         test("isArrayOfD", cArray);
403         test("isArrayOfD", dArray);
404     }
405 
406     @SuppressWarnings("unchecked")
arrayCopyTypeName(T[] original)407     public static <T> String arrayCopyTypeName(T[] original) {
408         Class<? extends T[]> newType = (Class<? extends T[]>) original.getClass();
409         if (newType == (Object) Object[].class) {
410             return Object[].class.getName();
411         } else {
412             return newType.getName();
413         }
414     }
415 
416     @Test
testArrayCopy()417     public void testArrayCopy() {
418         test("arrayCopyTypeName", (Object) new Object[]{"one", "two", "three"});
419         test("arrayCopyTypeName", (Object) new String[]{"one", "two", "three"});
420     }
421 
conditionalInstantiation(Object o)422     public int conditionalInstantiation(Object o) {
423         int total = 0;
424         if (o instanceof CharSequence) {
425             if (o instanceof StringBuilder || o instanceof String) {
426                 total = 9;
427             }
428             total += (o instanceof String ? 2 : 1);
429         }
430 
431         return total;
432     }
433 
434     @Test
testInstantiation()435     public void testInstantiation() {
436         test("conditionalInstantiation", "foo");
437         test("conditionalInstantiation", new StringBuilder());
438         test("conditionalInstantiation", 1);
439     }
440 
exactlyObject(Thread thread)441     public boolean exactlyObject(Thread thread) {
442         return thread != null && ((Object) thread).getClass() == Object.class;
443     }
444 
exactlyObjectArray(Thread[] threads)445     public boolean exactlyObjectArray(Thread[] threads) {
446         return threads != null && ((Object[]) threads).getClass() == Object[].class;
447     }
448 
exactlyString(Thread thread)449     public boolean exactlyString(Thread thread) {
450         return thread != null && ((Object) thread).getClass() == String.class;
451     }
452 
exactlyStringArray(Thread[] threads)453     public boolean exactlyStringArray(Thread[] threads) {
454         return threads != null && ((Object[]) threads).getClass() == String[].class;
455     }
456 
457     @SuppressWarnings("cast")
instanceofStringArray(Thread[] threads)458     public boolean instanceofStringArray(Thread[] threads) {
459         return threads != null && ((Object[]) threads) instanceof String[];
460     }
461 
462     @SuppressWarnings("cast")
instanceofString(Thread thread)463     public boolean instanceofString(Thread thread) {
464         return thread != null && ((Object) thread) instanceof String;
465     }
466 
467     @Test
testTypeCheck()468     public void testTypeCheck() {
469         testConstantReturn("exactlyObject", 0);
470         testConstantReturn("exactlyObjectArray", 0);
471         testConstantReturn("exactlyString", 0);
472         testConstantReturn("exactlyStringArray", 0);
473         testConstantReturn("instanceofString", 0);
474         testConstantReturn("instanceofStringArray", 0);
475     }
476 
testConstantReturn(String name, Object value)477     private void testConstantReturn(String name, Object value) {
478         StructuredGraph result = buildGraph(name);
479         ReturnNode ret = result.getNodes(ReturnNode.TYPE).first();
480         assertDeepEquals(1, result.getNodes(ReturnNode.TYPE).count());
481 
482         assertDeepEquals(true, ret.result().isConstant());
483         assertDeepEquals(value, ret.result().asJavaConstant().asBoxedPrimitive());
484     }
485 
486     @SuppressWarnings("try")
buildGraph(final String snippet)487     protected StructuredGraph buildGraph(final String snippet) {
488         DebugContext debug = getDebugContext();
489         try (DebugContext.Scope s = debug.scope("InstanceOfTest", getMetaAccess().lookupJavaMethod(getMethod(snippet)))) {
490             StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES, debug);
491             compile(graph.method(), graph);
492             debug.dump(DebugContext.BASIC_LEVEL, graph, snippet);
493             return graph;
494         } catch (Throwable e) {
495             throw debug.handle(e);
496         }
497     }
498 
499     static class Depth1 implements Cloneable {
500     }
501 
502     static class Depth2 extends Depth1 {
503     }
504 
505     static class Depth3 extends Depth2 {
506     }
507 
508     static class Depth4 extends Depth3 {
509     }
510 
511     static class Depth5 extends Depth4 {
512     }
513 
514     static class Depth6 extends Depth5 {
515     }
516 
517     static class Depth7 extends Depth6 {
518     }
519 
520     static class Depth8 extends Depth7 {
521     }
522 
523     static class Depth9 extends Depth8 {
524     }
525 
526     static class Depth10 extends Depth9 {
527     }
528 
529     static class Depth11 extends Depth10 {
530     }
531 
532     static class Depth12 extends Depth11 {
533     }
534 
535     static class Depth13 extends Depth12 {
536     }
537 
538     static class Depth14 extends Depth12 {
539     }
540 }
541