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 }