1 /* 2 * Copyright (c) 2013, 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 package org.graalvm.compiler.phases.verify; 24 25 import org.graalvm.compiler.core.common.type.ObjectStamp; 26 import org.graalvm.compiler.nodes.ParameterNode; 27 import org.graalvm.compiler.nodes.StructuredGraph; 28 import org.graalvm.compiler.nodes.ValueNode; 29 import org.graalvm.compiler.nodes.calc.ObjectEqualsNode; 30 import org.graalvm.compiler.nodes.type.StampTool; 31 import org.graalvm.compiler.phases.VerifyPhase; 32 import org.graalvm.compiler.phases.tiers.PhaseContext; 33 34 import jdk.vm.ci.meta.JavaField; 35 import jdk.vm.ci.meta.JavaKind; 36 import jdk.vm.ci.meta.JavaMethod; 37 import jdk.vm.ci.meta.JavaType; 38 import jdk.vm.ci.meta.MetaAccessProvider; 39 import jdk.vm.ci.meta.ResolvedJavaMethod; 40 import jdk.vm.ci.meta.ResolvedJavaType; 41 import jdk.vm.ci.meta.Signature; 42 43 /** 44 * For certain types, object identity should not be used for object equality check. This phase 45 * checks the correct usage of the given type. Equality checks with == or != (except null checks) 46 * results in an {@link AssertionError}. 47 */ 48 public class VerifyUsageWithEquals extends VerifyPhase<PhaseContext> { 49 50 @Override 51 public boolean checkContract() { 52 return false; 53 } 54 55 /** 56 * The type of values that must not use identity for testing object equality. 57 */ 58 private final Class<?> restrictedClass; 59 60 public VerifyUsageWithEquals(Class<?> restrictedClass) { 61 this.restrictedClass = restrictedClass; 62 assert !restrictedClass.isInterface() || isTrustedInterface(restrictedClass); 63 } 64 65 private static final Class<?>[] trustedInterfaceTypes = {JavaType.class, JavaField.class, JavaMethod.class}; 66 67 private static boolean isTrustedInterface(Class<?> cls) { 68 for (Class<?> trusted : trustedInterfaceTypes) { 69 if (trusted.isAssignableFrom(cls)) { 70 return true; 71 } 72 } 73 return false; 74 } 75 76 /** 77 * Determines whether the type of {@code node} is assignable to the {@link #restrictedClass}. 78 */ 79 private boolean isAssignableToRestrictedType(ValueNode node, MetaAccessProvider metaAccess) { 80 if (node.stamp() instanceof ObjectStamp) { 81 ResolvedJavaType restrictedType = metaAccess.lookupJavaType(restrictedClass); 82 ResolvedJavaType nodeType = StampTool.typeOrNull(node); 83 84 if (nodeType != null && restrictedType.isAssignableFrom(nodeType)) { 85 return true; 86 } 87 } 88 return false; 89 } 90 91 private static boolean isNullConstant(ValueNode node) { 92 return node.isConstant() && node.isNullConstant(); 93 } 94 95 private static boolean isEqualsMethod(ResolvedJavaMethod method) { 96 if (method.getName().equals("equals")) { 97 Signature sig = method.getSignature(); 98 if (sig.getReturnKind() == JavaKind.Boolean) { 99 if (sig.getParameterCount(false) == 1) { 100 ResolvedJavaType ptype = (ResolvedJavaType) sig.getParameterType(0, method.getDeclaringClass()); 101 if (ptype.isJavaLangObject()) { 102 return true; 103 } 104 105 } 106 } 107 } 108 return false; 109 } 110 111 private static boolean isThisParameter(ValueNode node) { 112 return node instanceof ParameterNode && ((ParameterNode) node).index() == 0; 113 } 114 115 /** 116 * Checks whether the type of {@code x} is assignable to the restricted type and that {@code y} 117 * is not a null constant. 118 */ 119 private boolean isIllegalUsage(ResolvedJavaMethod method, ValueNode x, ValueNode y, MetaAccessProvider metaAccess) { 120 if (isAssignableToRestrictedType(x, metaAccess) && !isNullConstant(y)) { 121 if (isEqualsMethod(method) && isThisParameter(x) || isThisParameter(y)) { 122 return false; 123 } 124 return true; 125 } 126 return false; 127 } 128 129 @Override 130 protected boolean verify(StructuredGraph graph, PhaseContext context) { 131 for (ObjectEqualsNode cn : graph.getNodes().filter(ObjectEqualsNode.class)) { 132 // bail out if we compare an object of type klass with == or != (except null checks) 133 ResolvedJavaMethod method = graph.method(); 134 ResolvedJavaType restrictedType = context.getMetaAccess().lookupJavaType(restrictedClass); 135 136 if (method.getDeclaringClass().equals(restrictedType)) { 137 // Allow violation in methods of the restricted type itself. 138 } else if (isIllegalUsage(method, cn.getX(), cn.getY(), context.getMetaAccess()) || isIllegalUsage(method, cn.getY(), cn.getX(), context.getMetaAccess())) { 139 throw new VerificationError("Verification of " + restrictedClass.getName() + " usage failed: Comparing " + cn.getX() + " and " + cn.getY() + " in " + method + 140 " must use .equals() for object equality, not '==' or '!='"); 141 } 142 } 143 return true; 144 } 145 }