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