UnbalancedMonitorsTest.java revision 12651:6ef01bd40ce2
1/*
2 * Copyright (c) 2015, 2016, 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 */
23package org.graalvm.compiler.core.test;
24
25import jdk.vm.ci.code.BailoutException;
26import jdk.vm.ci.meta.ResolvedJavaMethod;
27import jdk.internal.org.objectweb.asm.ClassWriter;
28import jdk.internal.org.objectweb.asm.Label;
29import jdk.internal.org.objectweb.asm.MethodVisitor;
30import jdk.internal.org.objectweb.asm.Opcodes;
31
32import static org.graalvm.compiler.core.common.CompilationIdentifier.INVALID_COMPILATION_ID;
33
34import org.junit.Test;
35
36import org.graalvm.compiler.java.GraphBuilderPhase;
37import org.graalvm.compiler.nodes.StructuredGraph;
38import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
39import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
40import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
41import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins;
42import org.graalvm.compiler.phases.OptimisticOptimizations;
43
44/**
45 * Exercise handling of unbalanced monitor operations by the parser. Algorithmically Graal assumes
46 * that locks are statically block structured but that isn't something enforced by the bytecodes. In
47 * HotSpot a dataflow is performed to ensure they are properly structured and methods with
48 * unstructured locking aren't compiled and fall back to the interpreter. Having the Graal parser
49 * handle this directly is simplifying for targets of Graal since they don't have to provide a data
50 * flow that checks this property.
51 */
52public class UnbalancedMonitorsTest extends GraalCompilerTest implements Opcodes {
53    private static final String CLASS_NAME = UnbalancedMonitorsTest.class.getName();
54    private static final String INNER_CLASS_NAME = CLASS_NAME + "$UnbalancedMonitors";
55    private static final String CLASS_NAME_INTERNAL = CLASS_NAME.replace('.', '/');
56    private static final String INNER_CLASS_NAME_INTERNAL = INNER_CLASS_NAME.replace('.', '/');
57
58    private static AsmLoader LOADER = new AsmLoader(UnbalancedMonitorsTest.class.getClassLoader());
59
60    @Test
61    public void runWrongOrder() throws Exception {
62        checkForBailout("wrongOrder");
63    }
64
65    @Test
66    public void runTooFewExits() throws Exception {
67        checkForBailout("tooFewExits");
68    }
69
70    @Test
71    public void runTooManyExits() throws Exception {
72        checkForBailout("tooManyExits");
73    }
74
75    @Test
76    public void runTooFewExitsExceptional() throws Exception {
77        checkForBailout("tooFewExitsExceptional");
78    }
79
80    @Test
81    public void runTooManyExitsExceptional() throws Exception {
82        checkForBailout("tooManyExitsExceptional");
83    }
84
85    private void checkForBailout(String name) throws ClassNotFoundException {
86        ResolvedJavaMethod method = getResolvedJavaMethod(LOADER.findClass(INNER_CLASS_NAME), name);
87        try {
88            StructuredGraph graph = new StructuredGraph(method, AllowAssumptions.NO, INVALID_COMPILATION_ID);
89            Plugins plugins = new Plugins(new InvocationPlugins(getMetaAccess()));
90            GraphBuilderConfiguration graphBuilderConfig = GraphBuilderConfiguration.getDefault(plugins).withEagerResolving(true);
91            OptimisticOptimizations optimisticOpts = OptimisticOptimizations.NONE;
92
93            GraphBuilderPhase.Instance graphBuilder = new GraphBuilderPhase.Instance(getMetaAccess(), getProviders().getStampProvider(), null, null, graphBuilderConfig, optimisticOpts, null);
94            graphBuilder.apply(graph);
95        } catch (BailoutException e) {
96            if (e.getMessage().contains("unbalanced monitors")) {
97                return;
98            }
99            throw e;
100        }
101        assertTrue("should have bailed out", false);
102    }
103
104    // @formatter:off
105    // Template class used with Bytecode Outline to generate ASM code
106    //    public static class UnbalancedMonitors {
107    //
108    //        public UnbalancedMonitors() {
109    //        }
110    //
111    //        public Object wrongOrder(Object a, Object b) {
112    //            synchronized (a) {
113    //                synchronized (b) {
114    //                    return b;
115    //                }
116    //            }
117    //        }
118    //
119    //        public Object tooFewExits(Object a, Object b) {
120    //            synchronized (a) {
121    //                synchronized (b) {
122    //                    return b;
123    //                }
124    //            }
125    //        }
126    //
127    //        public boolean tooFewExitsExceptional(Object a, Object b) {
128    //            synchronized (a) {
129    //                synchronized (b) {
130    //                    return b.equals(a);
131    //                }
132    //            }
133    //        }
134    //    }
135    // @formatter:on
136
137    public static byte[] generateClass() {
138
139        ClassWriter cw = new ClassWriter(0);
140
141        cw.visit(52, ACC_SUPER | ACC_PUBLIC, INNER_CLASS_NAME_INTERNAL, null, "java/lang/Object", null);
142
143        cw.visitSource("UnbalancedMonitorsTest.java", null);
144
145        cw.visitInnerClass(INNER_CLASS_NAME_INTERNAL, CLASS_NAME_INTERNAL, "UnbalancedMonitors", ACC_STATIC);
146
147        visitConstructor(cw);
148        visitWrongOrder(cw);
149        visitBlockStructured(cw, true, false);
150        visitBlockStructured(cw, true, true);
151        visitBlockStructured(cw, false, false);
152        visitBlockStructured(cw, false, true);
153        cw.visitEnd();
154
155        return cw.toByteArray();
156    }
157
158    private static void visitBlockStructured(ClassWriter cw, boolean normalReturnError, boolean tooMany) {
159        String name = (tooMany ? "tooMany" : "tooFew") + "Exits" + (normalReturnError ? "" : "Exceptional");
160        // Generate too many or too few exits down the either the normal or exceptional return paths
161        int exceptionalExitCount = normalReturnError ? 1 : (tooMany ? 2 : 0);
162        int normalExitCount = normalReturnError ? (tooMany ? 2 : 0) : 1;
163        MethodVisitor mv;
164        mv = cw.visitMethod(ACC_PUBLIC, name, "(Ljava/lang/Object;Ljava/lang/Object;)Z", null, null);
165        mv.visitCode();
166        Label l0 = new Label();
167        Label l1 = new Label();
168        Label l2 = new Label();
169        mv.visitTryCatchBlock(l0, l1, l2, null);
170        Label l3 = new Label();
171        mv.visitTryCatchBlock(l2, l3, l2, null);
172        Label l4 = new Label();
173        Label l5 = new Label();
174        Label l6 = new Label();
175        mv.visitTryCatchBlock(l4, l5, l6, null);
176        Label l7 = new Label();
177        mv.visitTryCatchBlock(l2, l7, l6, null);
178        Label l8 = new Label();
179        mv.visitLabel(l8);
180        mv.visitVarInsn(ALOAD, 1);
181        mv.visitInsn(DUP);
182        mv.visitVarInsn(ASTORE, 3);
183        mv.visitInsn(MONITORENTER);
184        mv.visitLabel(l4);
185        mv.visitVarInsn(ALOAD, 2);
186        mv.visitInsn(DUP);
187        mv.visitVarInsn(ASTORE, 4);
188        mv.visitInsn(MONITORENTER);
189        mv.visitLabel(l0);
190        mv.visitVarInsn(ALOAD, 2);
191        mv.visitVarInsn(ALOAD, 1);
192        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "equals", "(Ljava/lang/Object;)Z", false);
193        mv.visitVarInsn(ALOAD, 4);
194        mv.visitInsn(MONITOREXIT);
195        mv.visitLabel(l1);
196        for (int i = 0; i < normalExitCount; i++) {
197            mv.visitVarInsn(ALOAD, 3);
198            mv.visitInsn(MONITOREXIT);
199        }
200        mv.visitLabel(l5);
201        mv.visitInsn(IRETURN);
202        mv.visitLabel(l2);
203        mv.visitFrame(Opcodes.F_FULL, 5, new Object[]{INNER_CLASS_NAME_INTERNAL, "java/lang/Object", "java/lang/Object", "java/lang/Object",
204                        "java/lang/Object"}, 1, new Object[]{"java/lang/Throwable"});
205        mv.visitVarInsn(ALOAD, 4);
206        mv.visitInsn(MONITOREXIT);
207        mv.visitLabel(l3);
208        mv.visitInsn(ATHROW);
209        mv.visitLabel(l6);
210        mv.visitFrame(Opcodes.F_FULL, 4, new Object[]{INNER_CLASS_NAME_INTERNAL, "java/lang/Object", "java/lang/Object", "java/lang/Object"}, 1,
211                        new Object[]{"java/lang/Throwable"});
212        for (int i = 0; i < exceptionalExitCount; i++) {
213            mv.visitVarInsn(ALOAD, 3);
214            mv.visitInsn(MONITOREXIT);
215        }
216        mv.visitLabel(l7);
217        mv.visitInsn(ATHROW);
218        Label l9 = new Label();
219        mv.visitLabel(l9);
220        mv.visitMaxs(2, 5);
221        mv.visitEnd();
222    }
223
224    private static void visitWrongOrder(ClassWriter cw) {
225        MethodVisitor mv;
226        mv = cw.visitMethod(ACC_PUBLIC, "wrongOrder", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", null, null);
227        mv.visitCode();
228        Label l0 = new Label();
229        Label l1 = new Label();
230        Label l2 = new Label();
231        mv.visitTryCatchBlock(l0, l1, l2, null);
232        Label l3 = new Label();
233        mv.visitTryCatchBlock(l2, l3, l2, null);
234        Label l4 = new Label();
235        Label l5 = new Label();
236        Label l6 = new Label();
237        mv.visitTryCatchBlock(l4, l5, l6, null);
238        Label l7 = new Label();
239        mv.visitTryCatchBlock(l2, l7, l6, null);
240        Label l8 = new Label();
241        mv.visitLabel(l8);
242        mv.visitVarInsn(ALOAD, 1);
243        mv.visitInsn(DUP);
244        mv.visitVarInsn(ASTORE, 3);
245        mv.visitInsn(MONITORENTER);
246        mv.visitLabel(l4);
247        mv.visitVarInsn(ALOAD, 2);
248        mv.visitInsn(DUP);
249        mv.visitVarInsn(ASTORE, 4);
250        mv.visitInsn(MONITORENTER);
251        mv.visitLabel(l0);
252        mv.visitVarInsn(ALOAD, 2);
253        mv.visitVarInsn(ALOAD, 3);
254        mv.visitInsn(MONITOREXIT);
255        mv.visitLabel(l1);
256        // Swapped exit order with exit above
257        mv.visitVarInsn(ALOAD, 4);
258        mv.visitInsn(MONITOREXIT);
259        mv.visitLabel(l5);
260        mv.visitInsn(ARETURN);
261        mv.visitLabel(l2);
262        mv.visitFrame(Opcodes.F_FULL, 5, new Object[]{INNER_CLASS_NAME_INTERNAL, "java/lang/Object", "java/lang/Object", "java/lang/Object",
263                        "java/lang/Object"}, 1, new Object[]{"java/lang/Throwable"});
264        mv.visitVarInsn(ALOAD, 4);
265        mv.visitInsn(MONITOREXIT);
266        mv.visitLabel(l3);
267        mv.visitInsn(ATHROW);
268        mv.visitLabel(l6);
269        mv.visitFrame(Opcodes.F_FULL, 4, new Object[]{INNER_CLASS_NAME_INTERNAL, "java/lang/Object", "java/lang/Object", "java/lang/Object"}, 1,
270                        new Object[]{"java/lang/Throwable"});
271        mv.visitVarInsn(ALOAD, 3);
272        mv.visitInsn(MONITOREXIT);
273        mv.visitLabel(l7);
274        mv.visitInsn(ATHROW);
275        Label l9 = new Label();
276        mv.visitLabel(l9);
277        mv.visitMaxs(2, 5);
278        mv.visitEnd();
279    }
280
281    private static void visitConstructor(ClassWriter cw) {
282        MethodVisitor mv;
283        mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
284        mv.visitCode();
285        Label l0 = new Label();
286        mv.visitLabel(l0);
287        mv.visitVarInsn(ALOAD, 0);
288        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
289        Label l1 = new Label();
290        mv.visitLabel(l1);
291        mv.visitInsn(RETURN);
292        Label l2 = new Label();
293        mv.visitLabel(l2);
294        mv.visitMaxs(1, 1);
295        mv.visitEnd();
296    }
297
298    public static class AsmLoader extends ClassLoader {
299        Class<?> loaded;
300
301        public AsmLoader(ClassLoader parent) {
302            super(parent);
303        }
304
305        @Override
306        protected Class<?> findClass(String name) throws ClassNotFoundException {
307            if (name.equals(INNER_CLASS_NAME)) {
308                if (loaded != null) {
309                    return loaded;
310                }
311                byte[] bytes = generateClass();
312                return (loaded = defineClass(name, bytes, 0, bytes.length));
313            } else {
314                return super.findClass(name);
315            }
316        }
317    }
318}
319