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