VerifyUsageWithEquals.java revision 12651:6ef01bd40ce2
12741Swollman/* 22741Swollman * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. 32741Swollman * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 42741Swollman * 52741Swollman * This code is free software; you can redistribute it and/or modify it 62741Swollman * under the terms of the GNU General Public License version 2 only, as 72741Swollman * published by the Free Software Foundation. 82741Swollman * 92741Swollman * This code is distributed in the hope that it will be useful, but WITHOUT 102741Swollman * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 112741Swollman * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 122741Swollman * version 2 for more details (a copy is included in the LICENSE file that 132741Swollman * accompanied this code). 142741Swollman * 152741Swollman * You should have received a copy of the GNU General Public License version 162741Swollman * 2 along with this work; if not, write to the Free Software Foundation, 172741Swollman * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 182741Swollman * 192741Swollman * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 202741Swollman * or visit www.oracle.com if you need additional information or have any 212741Swollman * questions. 222741Swollman */ 232741Swollmanpackage org.graalvm.compiler.phases.verify; 242741Swollman 252741Swollmanimport org.graalvm.compiler.core.common.type.ObjectStamp; 262741Swollmanimport org.graalvm.compiler.nodes.ParameterNode; 272741Swollmanimport org.graalvm.compiler.nodes.StructuredGraph; 282741Swollmanimport org.graalvm.compiler.nodes.ValueNode; 292741Swollmanimport org.graalvm.compiler.nodes.calc.ObjectEqualsNode; 302741Swollmanimport org.graalvm.compiler.nodes.type.StampTool; 312741Swollmanimport org.graalvm.compiler.phases.VerifyPhase; 322741Swollmanimport org.graalvm.compiler.phases.tiers.PhaseContext; 3350476Speter 342741Swollmanimport jdk.vm.ci.meta.JavaField; 35237573Sissyl0import jdk.vm.ci.meta.JavaKind; 362741Swollmanimport jdk.vm.ci.meta.JavaMethod; 372741Swollmanimport jdk.vm.ci.meta.JavaType; 382741Swollmanimport jdk.vm.ci.meta.MetaAccessProvider; 392741Swollmanimport jdk.vm.ci.meta.ResolvedJavaMethod; 402741Swollmanimport jdk.vm.ci.meta.ResolvedJavaType; 4159460Sphantomimport jdk.vm.ci.meta.Signature; 4259460Sphantom 432741Swollman/** 4484306Sru * For certain types, object identity should not be used for object equality check. This phase 452741Swollman * checks the correct usage of the given type. Equality checks with == or != (except null checks) 46101936Srobert * results in an {@link AssertionError}. 47103012Stjr */ 48101936Srobertpublic class VerifyUsageWithEquals extends VerifyPhase<PhaseContext> { 49103012Stjr 50103012Stjr @Override 51101936Srobert public boolean checkContract() { 52237573Sissyl0 return false; 53237573Sissyl0 } 542741Swollman 552741Swollman /** 562741Swollman * The type of values that must not use identity for testing object equality. 572741Swollman */ 582741Swollman private final Class<?> restrictedClass; 592741Swollman 602741Swollman public VerifyUsageWithEquals(Class<?> restrictedClass) { 612741Swollman this.restrictedClass = restrictedClass; 622741Swollman assert !restrictedClass.isInterface() || isTrustedInterface(restrictedClass); 63237573Sissyl0 } 64237573Sissyl0 65237573Sissyl0 private static final Class<?>[] trustedInterfaceTypes = {JavaType.class, JavaField.class, JavaMethod.class}; 66237573Sissyl0 67237573Sissyl0 private static boolean isTrustedInterface(Class<?> cls) { 682741Swollman for (Class<?> trusted : trustedInterfaceTypes) { 692741Swollman if (trusted.isAssignableFrom(cls)) { 702741Swollman return true; 712741Swollman } 722741Swollman } 732741Swollman return false; 742741Swollman } 752741Swollman 762741Swollman /** 772741Swollman * Determines whether the type of {@code node} is assignable to the {@link #restrictedClass}. 782741Swollman */ 792741Swollman private boolean isAssignableToRestrictedType(ValueNode node, MetaAccessProvider metaAccess) { 8024426Simp if (node.stamp() instanceof ObjectStamp) { 812741Swollman ResolvedJavaType restrictedType = metaAccess.lookupJavaType(restrictedClass); 8236854Sdanny ResolvedJavaType nodeType = StampTool.typeOrNull(node); 832741Swollman 842741Swollman if (nodeType != null && restrictedType.isAssignableFrom(nodeType)) { 852741Swollman return true; 8636854Sdanny } 8797682Sarchie } 882741Swollman return false; 8936854Sdanny } 9036854Sdanny 912741Swollman private static boolean isNullConstant(ValueNode node) { 922741Swollman return node.isConstant() && node.isNullConstant(); 939989Sache } 942741Swollman 959989Sache private static boolean isEqualsMethod(ResolvedJavaMethod method) { 9674412Sache if (method.getName().equals("equals")) { 972741Swollman Signature sig = method.getSignature(); 989989Sache if (sig.getReturnKind() == JavaKind.Boolean) { 999989Sache if (sig.getParameterCount(false) == 1) { 1009989Sache ResolvedJavaType ptype = (ResolvedJavaType) sig.getParameterType(0, method.getDeclaringClass()); 10174412Sache if (ptype.isJavaLangObject()) { 1022741Swollman return true; 1039989Sache } 1049989Sache 1052741Swollman } 10667498Sache } 1079989Sache } 1082741Swollman return false; 1092741Swollman } 1102741Swollman 1112741Swollman private static boolean isThisParameter(ValueNode node) { 112140081Sru return node instanceof ParameterNode && ((ParameterNode) node).index() == 0; 1139989Sache } 1149989Sache 11553960Sache /** 1169989Sache * Checks whether the type of {@code x} is assignable to the restricted type and that {@code y} 1179989Sache * is not a null constant. 1189989Sache */ 11953960Sache private boolean isIllegalUsage(ResolvedJavaMethod method, ValueNode x, ValueNode y, MetaAccessProvider metaAccess) { 12053960Sache if (isAssignableToRestrictedType(x, metaAccess) && !isNullConstant(y)) { 121148580Skeramida if (isEqualsMethod(method) && isThisParameter(x) || isThisParameter(y)) { 12274578Sache return false; 12353959Sache } 1242741Swollman return true; 125165357Sjmg } 1262741Swollman return false; 12774412Sache } 12874412Sache 12974412Sache @Override 13030089Shelbig protected boolean verify(StructuredGraph graph, PhaseContext context) { 13130089Shelbig for (ObjectEqualsNode cn : graph.getNodes().filter(ObjectEqualsNode.class)) { 13230089Shelbig // bail out if we compare an object of type klass with == or != (except null checks) 13330089Shelbig ResolvedJavaMethod method = graph.method(); 13430089Shelbig ResolvedJavaType restrictedType = context.getMetaAccess().lookupJavaType(restrictedClass); 13530089Shelbig 13630089Shelbig if (method.getDeclaringClass().equals(restrictedType)) { 13730089Shelbig // Allow violation in methods of the restricted type itself. 1382741Swollman } else if (isIllegalUsage(method, cn.getX(), cn.getY(), context.getMetaAccess()) || isIllegalUsage(method, cn.getY(), cn.getX(), context.getMetaAccess())) { 1392741Swollman throw new VerificationError("Verification of " + restrictedClass.getName() + " usage failed: Comparing " + cn.getX() + " and " + cn.getY() + " in " + method + 1409989Sache " must use .equals() for object equality, not '==' or '!='"); 141140081Sru } 142140081Sru } 1432741Swollman return true; 1442741Swollman } 1452741Swollman} 1462741Swollman