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