1 /* 2 * Copyright (c) 2013, 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 package org.graalvm.compiler.phases.verify; 24 25 import java.util.ArrayList; 26 import java.util.List; 27 28 import org.graalvm.compiler.debug.Debug; 29 import org.graalvm.compiler.debug.DebugMethodMetrics; 30 import org.graalvm.compiler.debug.GraalError; 31 import org.graalvm.compiler.graph.Node; 32 import org.graalvm.compiler.graph.NodeInputList; 33 import org.graalvm.compiler.nodes.CallTargetNode; 34 import org.graalvm.compiler.nodes.Invoke; 35 import org.graalvm.compiler.nodes.StructuredGraph; 36 import org.graalvm.compiler.nodes.java.MethodCallTargetNode; 37 import org.graalvm.compiler.nodes.java.NewArrayNode; 38 import org.graalvm.compiler.nodes.java.StoreIndexedNode; 39 import org.graalvm.compiler.phases.VerifyPhase; 40 import org.graalvm.compiler.phases.tiers.PhaseContext; 41 42 import jdk.vm.ci.meta.ResolvedJavaMethod; 43 import jdk.vm.ci.meta.ResolvedJavaType; 44 45 /** 46 * 47 * Verifies that call sites calling one of the methods in {@link Debug} use them correctly. Correct 48 * usage of the methods in {@link Debug} requires call sites to not eagerly evaluate their 49 * arguments. Additionally this phase verifies that no argument is the result of a call to 50 * {@link StringBuilder#toString()} or {@link StringBuffer#toString()}. Ideally the parameters at 51 * call sites of {@link Debug} are eliminated, and do not produce additional allocations, if 52 * {@link Debug#isDumpEnabled(int)} (or {@link Debug#isLogEnabled(int)}, ...) is {@code false}. 53 * 54 * Methods in {@link Debug} checked by this phase are various different versions of 55 * {@link Debug#log(String)} , {@link Debug#dump(int, Object, String)}, 56 * {@link Debug#logAndIndent(String)} and {@link Debug#verify(Object, String)}. 57 */ 58 public class VerifyDebugUsage extends VerifyPhase<PhaseContext> { 59 60 @Override 61 public boolean checkContract() { 62 return false; 63 } 64 65 @Override 66 protected boolean verify(StructuredGraph graph, PhaseContext context) { 67 ResolvedJavaType debugType = context.getMetaAccess().lookupJavaType(Debug.class); 68 ResolvedJavaType nodeType = context.getMetaAccess().lookupJavaType(Node.class); 69 ResolvedJavaType stringType = context.getMetaAccess().lookupJavaType(String.class); 70 ResolvedJavaType debugMethodMetricsType = context.getMetaAccess().lookupJavaType(DebugMethodMetrics.class); 71 ResolvedJavaType graalErrorType = context.getMetaAccess().lookupJavaType(GraalError.class); 72 73 for (MethodCallTargetNode t : graph.getNodes(MethodCallTargetNode.TYPE)) { 74 ResolvedJavaMethod callee = t.targetMethod(); 75 String calleeName = callee.getName(); 76 if (callee.getDeclaringClass().equals(debugType)) { 77 if (calleeName.equals("log") || calleeName.equals("logAndIndent") || calleeName.equals("verify") || calleeName.equals("dump")) { 78 verifyParameters(t, graph, t.arguments(), stringType, calleeName.equals("dump") ? 2 : 1); 79 } 80 } 81 if (callee.getDeclaringClass().isAssignableFrom(nodeType)) { 82 if (calleeName.equals("assertTrue") || calleeName.equals("assertFalse")) { 83 verifyParameters(t, graph, t.arguments(), stringType, 1); 84 } 85 } 86 if (callee.getDeclaringClass().equals(debugMethodMetricsType)) { 87 if (calleeName.equals("addToMetric") || calleeName.equals("getCurrentMetricValue") || calleeName.equals("incrementMetric")) { 88 verifyParameters(t, graph, t.arguments(), stringType, 1); 89 } 90 } 91 if (callee.getDeclaringClass().isAssignableFrom(graalErrorType) && !graph.method().getDeclaringClass().isAssignableFrom(graalErrorType)) { 92 if (calleeName.equals("guarantee")) { 93 verifyParameters(t, graph, t.arguments(), stringType, 0); 94 } 95 if (calleeName.equals("<init>") && callee.getSignature().getParameterCount(false) == 2) { 96 verifyParameters(t, graph, t.arguments(), stringType, 1); 97 } 98 } 99 } 100 return true; 101 } 102 103 private static void verifyParameters(MethodCallTargetNode callTarget, StructuredGraph callerGraph, NodeInputList<? extends Node> args, ResolvedJavaType stringType, int startArgIdx) { 104 if (callTarget.targetMethod().isVarArgs() && args.get(args.count() - 1) instanceof NewArrayNode) { 105 // unpack the arguments to the var args 106 List<Node> unpacked = new ArrayList<>(args.snapshot()); 107 NewArrayNode varArgParameter = (NewArrayNode) unpacked.remove(unpacked.size() - 1); 108 int firstVarArg = unpacked.size(); 109 for (Node usage : varArgParameter.usages()) { 110 if (usage instanceof StoreIndexedNode) { 111 StoreIndexedNode si = (StoreIndexedNode) usage; 112 unpacked.add(si.value()); 113 } 114 } 115 verifyParameters(callerGraph, callTarget.targetMethod(), unpacked, stringType, startArgIdx, firstVarArg); 116 } else { 117 verifyParameters(callerGraph, callTarget.targetMethod(), args, stringType, startArgIdx, -1); 118 } 119 } 120 121 private static void verifyParameters(StructuredGraph callerGraph, ResolvedJavaMethod verifiedCallee, List<? extends Node> args, ResolvedJavaType stringType, int startArgIdx, int varArgsIndex) { 122 int argIdx = startArgIdx; 123 int varArgsElementIndex = 0; 124 boolean reportVarArgs = false; 125 for (int i = 0; i < args.size(); i++) { 126 Node arg = args.get(i); 127 if (arg instanceof Invoke) { 128 reportVarArgs = varArgsIndex >= 0 && argIdx >= varArgsIndex; 129 Invoke invoke = (Invoke) arg; 130 CallTargetNode callTarget = invoke.callTarget(); 131 if (callTarget instanceof MethodCallTargetNode) { 132 ResolvedJavaMethod m = ((MethodCallTargetNode) callTarget).targetMethod(); 133 if (m.getName().equals("toString")) { 134 int bci = invoke.bci(); 135 int nonVarArgIdx = reportVarArgs ? argIdx - varArgsElementIndex : argIdx; 136 verifyStringConcat(callerGraph, verifiedCallee, bci, nonVarArgIdx, reportVarArgs ? varArgsElementIndex : -1, m); 137 verifyToStringCall(callerGraph, verifiedCallee, stringType, m, bci, nonVarArgIdx, reportVarArgs ? varArgsElementIndex : -1); 138 } else if (m.getName().equals("format")) { 139 int bci = invoke.bci(); 140 int nonVarArgIdx = reportVarArgs ? argIdx - varArgsElementIndex : argIdx; 141 verifyFormatCall(callerGraph, verifiedCallee, stringType, m, bci, nonVarArgIdx, reportVarArgs ? varArgsElementIndex : -1); 142 143 } 144 } 145 } 146 if (varArgsIndex >= 0 && i >= varArgsIndex) { 147 varArgsElementIndex++; 148 } 149 argIdx++; 150 } 151 } 152 153 /** 154 * Checks that a given call is not to {@link StringBuffer#toString()} or 155 * {@link StringBuilder#toString()}. 156 */ 157 private static void verifyStringConcat(StructuredGraph callerGraph, ResolvedJavaMethod verifiedCallee, int bci, int argIdx, int varArgsElementIndex, ResolvedJavaMethod callee) { 158 if (callee.getDeclaringClass().getName().equals("Ljava/lang/StringBuilder;") || callee.getDeclaringClass().getName().equals("Ljava/lang/StringBuffer;")) { 159 StackTraceElement e = callerGraph.method().asStackTraceElement(bci); 160 if (varArgsElementIndex >= 0) { 161 throw new VerificationError( 162 "In %s: element %d of parameter %d of call to %s appears to be a String concatenation expression.%n", e, varArgsElementIndex, argIdx, 163 verifiedCallee.format("%H.%n(%p)")); 164 } else { 165 throw new VerificationError( 166 "In %s: parameter %d of call to %s appears to be a String concatenation expression.%n", e, argIdx, verifiedCallee.format("%H.%n(%p)")); 167 } 168 } 169 } 170 171 /** 172 * Checks that a given call is not to {@link Object#toString()}. 173 */ 174 private static void verifyToStringCall(StructuredGraph callerGraph, ResolvedJavaMethod verifiedCallee, ResolvedJavaType stringType, ResolvedJavaMethod callee, int bci, int argIdx, 175 int varArgsElementIndex) { 176 if (callee.getSignature().getParameterCount(false) == 0 && callee.getSignature().getReturnType(callee.getDeclaringClass()).equals(stringType)) { 177 StackTraceElement e = callerGraph.method().asStackTraceElement(bci); 178 if (varArgsElementIndex >= 0) { 179 throw new VerificationError( 180 "In %s: element %d of parameter %d of call to %s is a call to toString() which is redundant (the callee will do it) and forces unnecessary eager evaluation.", 181 e, varArgsElementIndex, argIdx, verifiedCallee.format("%H.%n(%p)")); 182 } else { 183 throw new VerificationError("In %s: parameter %d of call to %s is a call to toString() which is redundant (the callee will do it) and forces unnecessary eager evaluation.", e, argIdx, 184 verifiedCallee.format("%H.%n(%p)")); 185 } 186 } 187 } 188 189 /** 190 * Checks that a given call is not to {@link String#format(String, Object...)} or 191 * {@link String#format(java.util.Locale, String, Object...)}. 192 */ 193 private static void verifyFormatCall(StructuredGraph callerGraph, ResolvedJavaMethod verifiedCallee, ResolvedJavaType stringType, ResolvedJavaMethod callee, int bci, int argIdx, 194 int varArgsElementIndex) { 195 if (callee.getDeclaringClass().equals(stringType) && callee.getSignature().getReturnType(callee.getDeclaringClass()).equals(stringType)) { 196 StackTraceElement e = callerGraph.method().asStackTraceElement(bci); 197 if (varArgsElementIndex >= 0) { 198 throw new VerificationError( 199 "In %s: element %d of parameter %d of call to %s is a call to String.format() which is redundant (%s does formatting) and forces unnecessary eager evaluation.", 200 e, varArgsElementIndex, argIdx, verifiedCallee.format("%H.%n(%p)"), verifiedCallee.format("%h.%n")); 201 } else { 202 throw new VerificationError("In %s: parameter %d of call to %s is a call to String.format() which is redundant (%s does formatting) and forces unnecessary eager evaluation.", e, 203 argIdx, 204 verifiedCallee.format("%H.%n(%p)"), verifiedCallee.format("%h.%n")); 205 } 206 } 207 } 208 }