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