1/*
2 * Copyright (c) 2011, 2016, 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 */
23package org.graalvm.compiler.replacements.test;
24
25import java.util.Arrays;
26import java.util.HashMap;
27import java.util.List;
28import java.util.Map;
29import java.util.TreeMap;
30
31import org.junit.Test;
32
33import org.graalvm.compiler.debug.Debug;
34import org.graalvm.compiler.debug.Debug.Scope;
35import org.graalvm.compiler.nodes.IfNode;
36import org.graalvm.compiler.nodes.ReturnNode;
37import org.graalvm.compiler.nodes.StructuredGraph;
38import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
39import org.graalvm.compiler.nodes.java.InstanceOfNode;
40import org.graalvm.compiler.phases.common.AbstractInliningPhase;
41
42import jdk.vm.ci.code.site.Call;
43import jdk.vm.ci.code.site.Mark;
44import jdk.vm.ci.code.site.Site;
45import jdk.vm.ci.meta.JavaTypeProfile;
46
47/**
48 * Tests the implementation of instanceof, allowing profiling information to be manually specified.
49 */
50public class InstanceOfTest extends TypeCheckTest {
51
52    public InstanceOfTest() {
53        getSuites().getHighTier().findPhase(AbstractInliningPhase.class).remove();
54    }
55
56    @Override
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
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
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
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
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
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
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
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
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
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
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
176    public static boolean isString(Object o) {
177        return o instanceof String;
178    }
179
180    public static int isStringInt(Object o) {
181        if (o instanceof String) {
182            return id(1);
183        }
184        return id(0);
185    }
186
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
194    public static int id(int value) {
195        return value;
196    }
197
198    public static boolean isThrowable(Object o) {
199        return ((Throwable) o) instanceof Exception;
200    }
201
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
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
217    public static boolean isMap(Object o) {
218        return o instanceof Map;
219    }
220
221    public static int isMapInt(Object o) {
222        if (o instanceof Map) {
223            return id(1);
224        }
225        return id(0);
226    }
227
228    public static boolean isDepth12(Object o) {
229        return o instanceof Depth12;
230    }
231
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
243        MySite(int offset) {
244            this.offset = offset;
245        }
246    }
247
248    static class MyMark extends MySite {
249
250        MyMark(int offset) {
251            super(offset);
252        }
253    }
254
255    abstract static class MySafepoint extends MySite {
256
257        MySafepoint(int offset) {
258            super(offset);
259        }
260    }
261
262    static class MyCall extends MySafepoint {
263
264        MyCall(int offset) {
265            super(offset);
266        }
267    }
268
269    @Test
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
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
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
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
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
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
349    public static boolean isArrayOfA(Object o) {
350        return o instanceof A[];
351    }
352
353    public static boolean isArrayOfB(Object o) {
354        return o instanceof B[];
355    }
356
357    public static boolean isArrayOfC(Object o) {
358        return o instanceof C[];
359    }
360
361    public static boolean isArrayOfD(Object o) {
362        return o instanceof D[];
363    }
364
365    @Test
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")
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
417    public void testArrayCopy() {
418        test("arrayCopyTypeName", (Object) new Object[]{"one", "two", "three"});
419        test("arrayCopyTypeName", (Object) new String[]{"one", "two", "three"});
420    }
421
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
435    public void testInstantiation() {
436        test("conditionalInstantiation", "foo");
437        test("conditionalInstantiation", new StringBuilder());
438        test("conditionalInstantiation", 1);
439    }
440
441    public boolean exactlyObject(Thread thread) {
442        return thread != null && ((Object) thread).getClass() == Object.class;
443    }
444
445    public boolean exactlyObjectArray(Thread[] threads) {
446        return threads != null && ((Object[]) threads).getClass() == Object[].class;
447    }
448
449    public boolean exactlyString(Thread thread) {
450        return thread != null && ((Object) thread).getClass() == String.class;
451    }
452
453    public boolean exactlyStringArray(Thread[] threads) {
454        return threads != null && ((Object[]) threads).getClass() == String[].class;
455    }
456
457    @SuppressWarnings("cast")
458    public boolean instanceofStringArray(Thread[] threads) {
459        return threads != null && ((Object[]) threads) instanceof String[];
460    }
461
462    @SuppressWarnings("cast")
463    public boolean instanceofString(Thread thread) {
464        return thread != null && ((Object) thread) instanceof String;
465    }
466
467    @Test
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
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")
487    protected StructuredGraph buildGraph(final String snippet) {
488        try (Scope s = Debug.scope("InstanceOfTest", getMetaAccess().lookupJavaMethod(getMethod(snippet)))) {
489            StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
490            compile(graph.method(), graph);
491            Debug.dump(Debug.BASIC_LOG_LEVEL, graph, snippet);
492            return graph;
493        } catch (Throwable e) {
494            throw Debug.handle(e);
495        }
496    }
497
498    static class Depth1 implements Cloneable {
499    }
500
501    static class Depth2 extends Depth1 {
502    }
503
504    static class Depth3 extends Depth2 {
505    }
506
507    static class Depth4 extends Depth3 {
508    }
509
510    static class Depth5 extends Depth4 {
511    }
512
513    static class Depth6 extends Depth5 {
514    }
515
516    static class Depth7 extends Depth6 {
517    }
518
519    static class Depth8 extends Depth7 {
520    }
521
522    static class Depth9 extends Depth8 {
523    }
524
525    static class Depth10 extends Depth9 {
526    }
527
528    static class Depth11 extends Depth10 {
529    }
530
531    static class Depth12 extends Depth11 {
532    }
533
534    static class Depth13 extends Depth12 {
535    }
536
537    static class Depth14 extends Depth12 {
538    }
539}
540