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