1 /* 2 * Copyright (c) 2014, 2015, 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 24 /* 25 * @test NullCheckDroppingsTest 26 * @bug 8054492 27 * @summary Casting can result in redundant null checks in generated code 28 * @library /testlibrary /test/lib 29 * @modules java.base/jdk.internal.misc 30 * java.management 31 * 32 * @build ClassFileInstaller sun.hotspot.WhiteBox jdk.test.lib.* 33 * @build compiler.intrinsics.klass.CastNullCheckDroppingsTest 34 * @run driver ClassFileInstaller sun.hotspot.WhiteBox 35 * sun.hotspot.WhiteBox$WhiteBoxPermission 36 * jdk.test.lib.Platform 37 * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 38 * -Xmixed -XX:-BackgroundCompilation -XX:-TieredCompilation -XX:CompileThreshold=1000 39 * -XX:CompileCommand=exclude,compiler.intrinsics.klass.CastNullCheckDroppingsTest::runTest 40 * compiler.intrinsics.klass.CastNullCheckDroppingsTest 41 */ 42 43 package compiler.intrinsics.klass; 44 45 import jdk.test.lib.Platform; 46 import sun.hotspot.WhiteBox; 47 import sun.hotspot.code.NMethod; 48 49 import java.lang.invoke.MethodHandle; 50 import java.lang.invoke.MethodHandles; 51 import java.lang.invoke.MethodType; 52 import java.lang.reflect.Method; 53 import java.util.function.BiFunction; 54 55 public class CastNullCheckDroppingsTest { 56 57 private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); 58 59 static final BiFunction<Class, Object, Object> fCast = (c, o) -> c.cast(o); 60 61 static final MethodHandle SET_SSINK; 62 static final MethodHandle MH_CAST; 63 64 static { 65 try { 66 SET_SSINK = MethodHandles.lookup().findSetter(CastNullCheckDroppingsTest.class, "ssink", String.class); 67 MH_CAST = MethodHandles.lookup().findVirtual(Class.class, 68 "cast", 69 MethodType.methodType(Object.class, Object.class)); 70 } 71 catch (Exception e) { 72 throw new Error(e); 73 } 74 } 75 76 static volatile String svalue = "A"; 77 static volatile String snull = null; 78 static volatile Integer iobj = new Integer(0); 79 static volatile int[] arr = new int[2]; 80 static volatile Class objClass = String.class; 81 static volatile Class nullClass = null; 82 83 String ssink; 84 Integer isink; 85 int[] asink; 86 87 public static void main(String[] args) throws Exception { 88 89 // Only test C2 in Server VM 90 if (!Platform.isServer()) { 91 return; 92 } 93 // Make sure background compilation is disabled 94 if (WHITE_BOX.getBooleanVMFlag("BackgroundCompilation")) { 95 throw new AssertionError("Background compilation enabled"); 96 } 97 // Make sure Tiered compilation is disabled 98 if (WHITE_BOX.getBooleanVMFlag("TieredCompilation")) { 99 throw new AssertionError("Tiered compilation enabled"); 100 } 101 102 Method methodClassCast = CastNullCheckDroppingsTest.class.getDeclaredMethod("testClassCast", String.class); 103 Method methodMHCast = CastNullCheckDroppingsTest.class.getDeclaredMethod("testMHCast", String.class); 104 Method methodMHSetter = CastNullCheckDroppingsTest.class.getDeclaredMethod("testMHSetter", String.class); 105 Method methodFunction = CastNullCheckDroppingsTest.class.getDeclaredMethod("testFunction", String.class); 106 107 CastNullCheckDroppingsTest t = new CastNullCheckDroppingsTest(); 108 t.runTest(methodClassCast, false); 109 t.runTest(methodMHCast, false); 110 t.runTest(methodMHSetter, false); 111 t.runTest(methodFunction, false); 112 113 // Edge cases 114 Method methodClassCastNull = CastNullCheckDroppingsTest.class.getDeclaredMethod("testClassCastNull", String.class); 115 Method methodNullClassCast = CastNullCheckDroppingsTest.class.getDeclaredMethod("testNullClassCast", String.class); 116 Method methodClassCastObj = CastNullCheckDroppingsTest.class.getDeclaredMethod("testClassCastObj", Object.class); 117 Method methodObjClassCast = CastNullCheckDroppingsTest.class.getDeclaredMethod("testObjClassCast", String.class); 118 Method methodVarClassCast = CastNullCheckDroppingsTest.class.getDeclaredMethod("testVarClassCast", String.class); 119 Method methodClassCastInt = CastNullCheckDroppingsTest.class.getDeclaredMethod("testClassCastInt", Object.class); 120 Method methodIntClassCast = CastNullCheckDroppingsTest.class.getDeclaredMethod("testIntClassCast", Object.class); 121 Method methodClassCastint = CastNullCheckDroppingsTest.class.getDeclaredMethod("testClassCastint", Object.class); 122 Method methodintClassCast = CastNullCheckDroppingsTest.class.getDeclaredMethod("testintClassCast", Object.class); 123 Method methodClassCastPrim = CastNullCheckDroppingsTest.class.getDeclaredMethod("testClassCastPrim", Object.class); 124 Method methodPrimClassCast = CastNullCheckDroppingsTest.class.getDeclaredMethod("testPrimClassCast", Object.class); 125 126 t.runTest(methodClassCastNull, false); 127 t.runTest(methodNullClassCast, false); 128 t.runTest(methodClassCastObj, false); 129 t.runTest(methodObjClassCast, true); 130 t.runTest(methodVarClassCast, true); 131 t.runTest(methodClassCastInt, false); 132 t.runTest(methodIntClassCast, true); 133 t.runTest(methodClassCastint, false); 134 t.runTest(methodintClassCast, false); 135 t.runTest(methodClassCastPrim, false); 136 t.runTest(methodPrimClassCast, true); 137 } 138 139 void testClassCast(String s) { 140 try { 141 ssink = String.class.cast(s); 142 } catch (Throwable t) { 143 throw new Error(t); 144 } 145 } 146 147 void testClassCastNull(String s) { 148 try { 149 ssink = String.class.cast(null); 150 } catch (Throwable t) { 151 throw new Error(t); 152 } 153 } 154 155 void testNullClassCast(String s) { 156 try { 157 ssink = (String)nullClass.cast(s); 158 throw new AssertionError("NullPointerException is not thrown"); 159 } catch (NullPointerException t) { 160 // Ignore NullPointerException 161 } catch (Throwable t) { 162 throw new Error(t); 163 } 164 } 165 166 void testClassCastObj(Object s) { 167 try { 168 ssink = String.class.cast(s); 169 } catch (Throwable t) { 170 throw new Error(t); 171 } 172 } 173 174 void testObjClassCast(String s) { 175 try { 176 ssink = (String)objClass.cast(s); 177 } catch (Throwable t) { 178 throw new Error(t); 179 } 180 } 181 182 void testVarClassCast(String s) { 183 Class cl = (s == null) ? null : String.class; 184 try { 185 ssink = (String)cl.cast(svalue); 186 if (s == null) { 187 throw new AssertionError("NullPointerException is not thrown"); 188 } 189 } catch (NullPointerException t) { 190 // Ignore NullPointerException 191 } catch (Throwable t) { 192 throw new Error(t); 193 } 194 } 195 196 void testClassCastInt(Object s) { 197 try { 198 ssink = String.class.cast(iobj); 199 throw new AssertionError("ClassCastException is not thrown"); 200 } catch (ClassCastException t) { 201 // Ignore ClassCastException: Cannot cast java.lang.Integer to java.lang.String 202 } catch (Throwable t) { 203 throw new Error(t); 204 } 205 } 206 207 void testIntClassCast(Object s) { 208 try { 209 isink = Integer.class.cast(s); 210 if (s != null) { 211 throw new AssertionError("ClassCastException is not thrown"); 212 } 213 } catch (ClassCastException t) { 214 // Ignore ClassCastException: Cannot cast java.lang.String to java.lang.Integer 215 } catch (Throwable t) { 216 throw new Error(t); 217 } 218 } 219 220 void testClassCastint(Object s) { 221 try { 222 ssink = String.class.cast(45); 223 throw new AssertionError("ClassCastException is not thrown"); 224 } catch (ClassCastException t) { 225 // Ignore ClassCastException: Cannot cast java.lang.Integer to java.lang.String 226 } catch (Throwable t) { 227 throw new Error(t); 228 } 229 } 230 231 void testintClassCast(Object s) { 232 try { 233 isink = int.class.cast(s); 234 if (s != null) { 235 throw new AssertionError("ClassCastException is not thrown"); 236 } 237 } catch (ClassCastException t) { 238 // Ignore ClassCastException: Cannot cast java.lang.String to java.lang.Integer 239 } catch (Throwable t) { 240 throw new Error(t); 241 } 242 } 243 244 void testClassCastPrim(Object s) { 245 try { 246 ssink = String.class.cast(arr); 247 throw new AssertionError("ClassCastException is not thrown"); 248 } catch (ClassCastException t) { 249 // Ignore ClassCastException: Cannot cast [I to java.lang.String 250 } catch (Throwable t) { 251 throw new Error(t); 252 } 253 } 254 255 void testPrimClassCast(Object s) { 256 try { 257 asink = int[].class.cast(s); 258 if (s != null) { 259 throw new AssertionError("ClassCastException is not thrown"); 260 } 261 } catch (ClassCastException t) { 262 // Ignore ClassCastException: Cannot cast java.lang.String to [I 263 } catch (Throwable t) { 264 throw new Error(t); 265 } 266 } 267 268 void testMHCast(String s) { 269 try { 270 ssink = (String) (Object) MH_CAST.invokeExact(String.class, (Object) s); 271 } catch (Throwable t) { 272 throw new Error(t); 273 } 274 } 275 276 void testMHSetter(String s) { 277 try { 278 SET_SSINK.invokeExact(this, s); 279 } catch (Throwable t) { 280 throw new Error(t); 281 } 282 } 283 284 void testFunction(String s) { 285 try { 286 ssink = (String) fCast.apply(String.class, s); 287 } catch (Throwable t) { 288 throw new Error(t); 289 } 290 } 291 292 void runTest(Method method, boolean deopt) { 293 if (method == null) { 294 throw new AssertionError("method was not found"); 295 } 296 // Ensure method is compiled 297 WHITE_BOX.testSetDontInlineMethod(method, true); 298 for (int i = 0; i < 3000; i++) { 299 try { 300 method.invoke(this, svalue); 301 } catch (Exception e) { 302 throw new Error("Unexpected exception: ", e); 303 } 304 } 305 NMethod nm = getNMethod(method); 306 307 // Passing null should cause a de-optimization 308 // if method is compiled with a null-check. 309 try { 310 method.invoke(this, snull); 311 } catch (Exception e) { 312 throw new Error("Unexpected exception: ", e); 313 } 314 checkDeoptimization(method, nm, deopt); 315 } 316 317 static NMethod getNMethod(Method test) { 318 // Because background compilation is disabled, method should now be compiled 319 if (!WHITE_BOX.isMethodCompiled(test)) { 320 throw new AssertionError(test + " not compiled"); 321 } 322 323 NMethod nm = NMethod.get(test, false); // not OSR nmethod 324 if (nm == null) { 325 throw new AssertionError(test + " missing nmethod?"); 326 } 327 if (nm.comp_level != 4) { 328 throw new AssertionError(test + " compiled by not C2: " + nm); 329 } 330 return nm; 331 } 332 333 static void checkDeoptimization(Method method, NMethod nmOrig, boolean deopt) { 334 // Check deoptimization event (intrinsic Class.cast() works). 335 if (WHITE_BOX.isMethodCompiled(method) == deopt) { 336 throw new AssertionError(method + " was" + (deopt ? " not" : "") + " deoptimized"); 337 } 338 if (deopt) { 339 return; 340 } 341 // Ensure no recompilation when no deoptimization is expected. 342 NMethod nm = NMethod.get(method, false); // not OSR nmethod 343 if (nm == null) { 344 throw new AssertionError(method + " missing nmethod?"); 345 } 346 if (nm.comp_level != 4) { 347 throw new AssertionError(method + " compiled by not C2: " + nm); 348 } 349 if (nm.compile_id != nmOrig.compile_id) { 350 throw new AssertionError(method + " was recompiled: old nmethod=" + nmOrig + ", new nmethod=" + nm); 351 } 352 } 353 }