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