1 /*
2  * Copyright (c) 2016, 2019, 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 package org.graalvm.compiler.core.test;
26 
27 import org.graalvm.compiler.core.common.PermanentBailoutException;
28 import org.graalvm.compiler.core.common.RetryableBailoutException;
29 import org.graalvm.compiler.debug.GraalError;
30 import org.graalvm.compiler.nodes.StructuredGraph;
31 import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
32 import org.graalvm.compiler.nodes.spi.CoreProviders;
33 import org.graalvm.compiler.phases.VerifyPhase;
34 
35 import jdk.vm.ci.code.BailoutException;
36 import jdk.vm.ci.meta.ResolvedJavaMethod;
37 import jdk.vm.ci.meta.ResolvedJavaType;
38 
39 public class VerifyBailoutUsage extends VerifyPhase<CoreProviders> {
40 
41     private static final String[] AllowedPackagePrefixes;
42 
getPackageName(Class<?> c)43     private static String getPackageName(Class<?> c) {
44         String classNameWithPackage = c.getName();
45         String simpleName = c.getSimpleName();
46         return classNameWithPackage.substring(0, classNameWithPackage.length() - simpleName.length() - 1);
47     }
48 
49     static {
50         try {
51             AllowedPackagePrefixes = new String[]{
52                             getPackageName(PermanentBailoutException.class),
53                             "jdk.vm.ci",
54 
55                             // Allows GraalTruffleRuntime.handleAnnotationFailure to throw
56                             // a BailoutException since the org.graalvm.compiler.truffle.runtime
57                             // project can not see the PermanentBailoutException or
58                             // RetryableBailoutException types.
59                             "org.graalvm.compiler.truffle.runtime"
60             };
61         } catch (Throwable t) {
62             throw new GraalError(t);
63         }
64     }
65 
matchesPrefix(String packageName)66     private static boolean matchesPrefix(String packageName) {
67         for (String allowedPackagePrefix : AllowedPackagePrefixes) {
68             if (packageName.startsWith(allowedPackagePrefix)) {
69                 return true;
70             }
71         }
72         return false;
73     }
74 
75     @Override
verify(StructuredGraph graph, CoreProviders context)76     protected void verify(StructuredGraph graph, CoreProviders context) {
77         final ResolvedJavaType bailoutType = context.getMetaAccess().lookupJavaType(BailoutException.class);
78         ResolvedJavaMethod caller = graph.method();
79         String holderQualified = caller.format("%H");
80         String holderUnqualified = caller.format("%h");
81         String packageName = holderQualified.substring(0, holderQualified.length() - holderUnqualified.length() - 1);
82         if (!matchesPrefix(packageName)) {
83             for (MethodCallTargetNode t : graph.getNodes(MethodCallTargetNode.TYPE)) {
84                 ResolvedJavaMethod callee = t.targetMethod();
85                 if (callee.getDeclaringClass().equals(bailoutType)) {
86                     // we only allow the getter
87                     if (!callee.getName().equals("isPermanent")) {
88                         throw new VerificationError("Call to %s at callsite %s is prohibited. Consider using %s for permanent bailouts or %s for retryables.", callee.format("%H.%n(%p)"),
89                                         caller.format("%H.%n(%p)"), PermanentBailoutException.class.getName(),
90                                         RetryableBailoutException.class.getName());
91                     }
92                 }
93             }
94         }
95     }
96 
97 }
98