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  */
  23 package org.graalvm.compiler.replacements.test;
  24 
  25 import java.util.Arrays;
  26 import java.util.HashMap;
  27 import java.util.List;
  28 import java.util.Map;
  29 import java.util.TreeMap;
  30 
  31 import org.junit.Test;
  32 
  33 import org.graalvm.compiler.debug.Debug;
  34 import org.graalvm.compiler.debug.Debug.Scope;
  35 import org.graalvm.compiler.nodes.IfNode;
  36 import org.graalvm.compiler.nodes.ReturnNode;
  37 import org.graalvm.compiler.nodes.StructuredGraph;
  38 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
  39 import org.graalvm.compiler.nodes.java.InstanceOfNode;
  40 import org.graalvm.compiler.phases.common.AbstractInliningPhase;
  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 
  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 }