001/*
002 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.replacements.test;
024
025import java.util.*;
026
027import jdk.internal.jvmci.code.CompilationResult.*;
028import com.oracle.graal.debug.*;
029import com.oracle.graal.debug.Debug.*;
030import jdk.internal.jvmci.meta.*;
031
032import org.junit.*;
033
034import com.oracle.graal.nodes.*;
035import com.oracle.graal.nodes.StructuredGraph.*;
036import com.oracle.graal.nodes.java.*;
037import com.oracle.graal.phases.common.*;
038import com.oracle.graal.replacements.test.CheckCastTest.Depth12;
039import com.oracle.graal.replacements.test.CheckCastTest.Depth13;
040import com.oracle.graal.replacements.test.CheckCastTest.Depth14;
041
042/**
043 * Tests the implementation of instanceof, allowing profiling information to be manually specified.
044 */
045public class InstanceOfTest extends TypeCheckTest {
046
047    public InstanceOfTest() {
048        getSuites().getHighTier().findPhase(AbstractInliningPhase.class).remove();
049    }
050
051    @Override
052    protected void replaceProfile(StructuredGraph graph, JavaTypeProfile profile) {
053        InstanceOfNode ion = graph.getNodes().filter(InstanceOfNode.class).first();
054        if (ion != null) {
055            LogicNode ionNew = graph.unique(InstanceOfNode.create(ion.type(), ion.getValue(), profile));
056            graph.replaceFloating(ion, ionNew);
057        }
058    }
059
060    @Test
061    public void test1() {
062        test("isString", profile(), "object");
063        test("isString", profile(String.class), "object");
064
065        test("isString", profile(), Object.class);
066        test("isString", profile(String.class), Object.class);
067    }
068
069    @Test
070    public void test2() {
071        test("isStringInt", profile(), "object");
072        test("isStringInt", profile(String.class), "object");
073
074        test("isStringInt", profile(), Object.class);
075        test("isStringInt", profile(String.class), Object.class);
076    }
077
078    @Test
079    public void test201() {
080        test("isStringIntComplex", profile(), "object");
081        test("isStringIntComplex", profile(String.class), "object");
082
083        test("isStringIntComplex", profile(), Object.class);
084        test("isStringIntComplex", profile(String.class), Object.class);
085    }
086
087    @Test
088    public void test3() {
089        Throwable throwable = new Exception();
090        test("isThrowable", profile(), throwable);
091        test("isThrowable", profile(Throwable.class), throwable);
092        test("isThrowable", profile(Exception.class, Error.class), throwable);
093
094        test("isThrowable", profile(), Object.class);
095        test("isThrowable", profile(Throwable.class), Object.class);
096        test("isThrowable", profile(Exception.class, Error.class), Object.class);
097    }
098
099    @Test
100    public void test301() {
101        onlyFirstIsException(new Exception(), new Error());
102        test("onlyFirstIsException", profile(), new Exception(), new Error());
103        test("onlyFirstIsException", profile(), new Error(), new Exception());
104        test("onlyFirstIsException", profile(), new Exception(), new Exception());
105        test("onlyFirstIsException", profile(), new Error(), new Error());
106    }
107
108    @Test
109    public void test4() {
110        Throwable throwable = new Exception();
111        test("isThrowableInt", profile(), throwable);
112        test("isThrowableInt", profile(Throwable.class), throwable);
113        test("isThrowableInt", profile(Exception.class, Error.class), throwable);
114
115        test("isThrowableInt", profile(), Object.class);
116        test("isThrowableInt", profile(Throwable.class), Object.class);
117        test("isThrowableInt", profile(Exception.class, Error.class), Object.class);
118    }
119
120    @Test
121    public void test5() {
122        Map<?, ?> map = new HashMap<>();
123        test("isMap", profile(), map);
124        test("isMap", profile(HashMap.class), map);
125        test("isMap", profile(TreeMap.class, HashMap.class), map);
126
127        test("isMap", profile(), Object.class);
128        test("isMap", profile(HashMap.class), Object.class);
129        test("isMap", profile(TreeMap.class, HashMap.class), Object.class);
130        test("isMap", profile(String.class, HashMap.class), Object.class);
131    }
132
133    @Test
134    public void test6() {
135        Map<?, ?> map = new HashMap<>();
136        test("isMapInt", profile(), map);
137        test("isMapInt", profile(HashMap.class), map);
138        test("isMapInt", profile(TreeMap.class, HashMap.class), map);
139
140        test("isMapInt", profile(), Object.class);
141        test("isMapInt", profile(HashMap.class), Object.class);
142        test("isMapInt", profile(TreeMap.class, HashMap.class), Object.class);
143    }
144
145    @Test
146    public void test7() {
147        Object o = new Depth13();
148        test("isDepth12", profile(), o);
149        test("isDepth12", profile(Depth13.class), o);
150        test("isDepth12", profile(Depth13.class, Depth14.class), o);
151
152        o = "not a depth";
153        test("isDepth12", profile(), o);
154        test("isDepth12", profile(Depth13.class), o);
155        test("isDepth12", profile(Depth13.class, Depth14.class), o);
156        test("isDepth12", profile(String.class, HashMap.class), o);
157    }
158
159    @Test
160    public void test8() {
161        Object o = new Depth13();
162        test("isDepth12Int", profile(), o);
163        test("isDepth12Int", profile(Depth13.class), o);
164        test("isDepth12Int", profile(Depth13.class, Depth14.class), o);
165
166        o = "not a depth";
167        test("isDepth12Int", profile(), o);
168        test("isDepth12Int", profile(Depth13.class), o);
169        test("isDepth12Int", profile(Depth13.class, Depth14.class), o);
170    }
171
172    public static boolean isString(Object o) {
173        return o instanceof String;
174    }
175
176    public static int isStringInt(Object o) {
177        if (o instanceof String) {
178            return id(1);
179        }
180        return id(0);
181    }
182
183    public static int isStringIntComplex(Object o) {
184        if (o instanceof String || o instanceof Integer) {
185            return id(o instanceof String ? 1 : 0);
186        }
187        return id(0);
188    }
189
190    public static int id(int value) {
191        return value;
192    }
193
194    public static boolean isThrowable(Object o) {
195        return ((Throwable) o) instanceof Exception;
196    }
197
198    public static int onlyFirstIsException(Throwable t1, Throwable t2) {
199        if (t1 instanceof Exception ^ t2 instanceof Exception) {
200            return t1 instanceof Exception ? 1 : -1;
201        }
202        return -1;
203    }
204
205    public static int isThrowableInt(Object o) {
206        int result = o instanceof Throwable ? 4 : 5;
207        if (o instanceof Throwable) {
208            return id(4);
209        }
210        return result;
211    }
212
213    public static boolean isMap(Object o) {
214        return o instanceof Map;
215    }
216
217    public static int isMapInt(Object o) {
218        if (o instanceof Map) {
219            return id(1);
220        }
221        return id(0);
222    }
223
224    public static boolean isDepth12(Object o) {
225        return o instanceof Depth12;
226    }
227
228    public static int isDepth12Int(Object o) {
229        if (o instanceof Depth12) {
230            return id(0);
231        }
232        return id(0);
233    }
234
235    abstract static class MySite {
236
237        final int offset;
238
239        MySite(int offset) {
240            this.offset = offset;
241        }
242    }
243
244    static class MyMark extends MySite {
245
246        MyMark(int offset) {
247            super(offset);
248        }
249    }
250
251    abstract static class MySafepoint extends MySite {
252
253        MySafepoint(int offset) {
254            super(offset);
255        }
256    }
257
258    static class MyCall extends MySafepoint {
259
260        MyCall(int offset) {
261            super(offset);
262        }
263    }
264
265    @Test
266    public void test9() {
267        MyCall callAt63 = new MyCall(63);
268        MyMark markAt63 = new MyMark(63);
269        test("compareMySites", callAt63, callAt63);
270        test("compareMySites", callAt63, markAt63);
271        test("compareMySites", markAt63, callAt63);
272        test("compareMySites", markAt63, markAt63);
273    }
274
275    public static int compareMySites(MySite s1, MySite s2) {
276        if (s1.offset == s2.offset && (s1 instanceof MyMark ^ s2 instanceof MyMark)) {
277            return s1 instanceof MyMark ? -1 : 1;
278        }
279        return s1.offset - s2.offset;
280    }
281
282    @Test
283    public void test10() {
284        Call callAt63 = new Call(null, 63, 5, true, null);
285        Mark markAt63 = new Mark(63, "1");
286        test("compareSites", callAt63, callAt63);
287        test("compareSites", callAt63, markAt63);
288        test("compareSites", markAt63, callAt63);
289        test("compareSites", markAt63, markAt63);
290    }
291
292    public static int compareSites(Site s1, Site s2) {
293        if (s1.pcOffset == s2.pcOffset && (s1 instanceof Mark ^ s2 instanceof Mark)) {
294            return s1 instanceof Mark ? -1 : 1;
295        }
296        return s1.pcOffset - s2.pcOffset;
297    }
298
299    /**
300     * This test exists to show the kind of pattern that is be optimizable by
301     * {@code removeIntermediateMaterialization()} in {@link IfNode}.
302     * <p>
303     * The test exists in this source file as the transformation was originally motivated by the
304     * need to remove use of special JumpNodes in the {@code InstanceOfSnippets}.
305     */
306    @Test
307    public void testRemoveIntermediateMaterialization() {
308        List<String> list = Arrays.asList("1", "2", "3", "4");
309        test("removeIntermediateMaterialization", profile(), list, "2", "yes", "no");
310        test("removeIntermediateMaterialization", profile(), list, null, "yes", "no");
311        test("removeIntermediateMaterialization", profile(), null, "2", "yes", "no");
312    }
313
314    public static String removeIntermediateMaterialization(List<Object> list, Object e, String a, String b) {
315        boolean test;
316        if (list == null || e == null) {
317            test = false;
318        } else {
319            test = false;
320            for (Object i : list) {
321                if (i.equals(e)) {
322                    test = true;
323                    break;
324                }
325            }
326        }
327        if (test) {
328            return a;
329        }
330        return b;
331    }
332
333    abstract static class A {
334    }
335
336    static class B extends A {
337    }
338
339    static class C extends B {
340    }
341
342    abstract static class D extends C {
343    }
344
345    public static boolean isArrayOfA(Object o) {
346        return o instanceof A[];
347    }
348
349    public static boolean isArrayOfB(Object o) {
350        return o instanceof B[];
351    }
352
353    public static boolean isArrayOfC(Object o) {
354        return o instanceof C[];
355    }
356
357    public static boolean isArrayOfD(Object o) {
358        return o instanceof D[];
359    }
360
361    @Test
362    public void testArray() {
363        Object aArray = new A[10];
364        test("isArrayOfA", aArray);
365
366        Object bArray = new B[10];
367        test("isArrayOfA", aArray);
368        test("isArrayOfA", bArray);
369        test("isArrayOfB", aArray);
370        test("isArrayOfB", bArray);
371
372        Object cArray = new C[10];
373        test("isArrayOfA", aArray);
374        test("isArrayOfA", bArray);
375        test("isArrayOfA", cArray);
376        test("isArrayOfB", aArray);
377        test("isArrayOfB", bArray);
378        test("isArrayOfB", cArray);
379        test("isArrayOfC", aArray);
380        test("isArrayOfC", bArray);
381        test("isArrayOfC", cArray);
382
383        Object dArray = new D[10];
384        test("isArrayOfA", aArray);
385        test("isArrayOfA", bArray);
386        test("isArrayOfA", cArray);
387        test("isArrayOfA", dArray);
388        test("isArrayOfB", aArray);
389        test("isArrayOfB", bArray);
390        test("isArrayOfB", cArray);
391        test("isArrayOfB", dArray);
392        test("isArrayOfC", aArray);
393        test("isArrayOfC", bArray);
394        test("isArrayOfC", cArray);
395        test("isArrayOfC", dArray);
396        test("isArrayOfD", aArray);
397        test("isArrayOfD", bArray);
398        test("isArrayOfD", cArray);
399        test("isArrayOfD", dArray);
400    }
401
402    @SuppressWarnings("unchecked")
403    public static <T> String arrayCopyTypeName(T[] original) {
404        Class<? extends T[]> newType = (Class<? extends T[]>) original.getClass();
405        if (newType == (Object) Object[].class) {
406            return Object[].class.getName();
407        } else {
408            return newType.getName();
409        }
410    }
411
412    @Test
413    public void testArrayCopy() {
414        test("arrayCopyTypeName", (Object) new Object[]{"one", "two", "three"});
415        test("arrayCopyTypeName", (Object) new String[]{"one", "two", "three"});
416    }
417
418    public int conditionalInstantiation(Object o) {
419        int total = 0;
420        if (o instanceof CharSequence) {
421            if (o instanceof StringBuilder || o instanceof String) {
422                total = 9;
423            }
424            total += (o instanceof String ? 2 : 1);
425        }
426
427        return total;
428    }
429
430    @Test
431    public void testInstantiation() {
432        test("conditionalInstantiation", "foo");
433        test("conditionalInstantiation", new StringBuilder());
434        test("conditionalInstantiation", 1);
435    }
436
437    public boolean exactlyObject(Thread thread) {
438        return thread != null && ((Object) thread).getClass() == Object.class;
439    }
440
441    public boolean exactlyObjectArray(Thread[] threads) {
442        return threads != null && ((Object[]) threads).getClass() == Object[].class;
443    }
444
445    public boolean exactlyString(Thread thread) {
446        return thread != null && ((Object) thread).getClass() == String.class;
447    }
448
449    public boolean exactlyStringArray(Thread[] threads) {
450        return threads != null && ((Object[]) threads).getClass() == String[].class;
451    }
452
453    @SuppressWarnings("cast")
454    public boolean instanceofStringArray(Thread[] threads) {
455        return threads != null && ((Object[]) threads) instanceof String[];
456    }
457
458    @SuppressWarnings("cast")
459    public boolean instanceofString(Thread thread) {
460        return thread != null && ((Object) thread) instanceof String;
461    }
462
463    /**
464     * {@link TypeCheckNode} and {@link InstanceOfNode} should be equivalently powerful when
465     * comparing disjoint types.
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    protected StructuredGraph buildGraph(final String snippet) {
487        try (Scope s = Debug.scope("InstanceOfTest", getMetaAccess().lookupJavaMethod(getMethod(snippet)))) {
488            StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
489            compile(graph.method(), graph);
490            Debug.dump(graph, snippet);
491            return graph;
492        } catch (Throwable e) {
493            throw Debug.handle(e);
494        }
495    }
496}