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 }