1
1
/*
2
- * Copyright (c) 2013, 2019 , Oracle and/or its affiliates. All rights reserved.
2
+ * Copyright (c) 2013, 2024 , Oracle and/or its affiliates. All rights reserved.
3
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
4
*
5
5
* This code is free software; you can redistribute it and/or modify it
24
24
*/
25
25
package jdk .graal .compiler .core .test ;
26
26
27
+ import java .util .Objects ;
28
+
29
+ import jdk .graal .compiler .api .replacements .SnippetReflectionProvider ;
27
30
import jdk .graal .compiler .core .common .type .ObjectStamp ;
28
31
import jdk .graal .compiler .nodes .Invoke ;
29
32
import jdk .graal .compiler .nodes .NodeView ;
36
39
import jdk .graal .compiler .nodes .spi .UncheckedInterfaceProvider ;
37
40
import jdk .graal .compiler .nodes .type .StampTool ;
38
41
import jdk .graal .compiler .phases .VerifyPhase ;
39
-
42
+ import jdk .vm .ci .meta .ConstantReflectionProvider ;
43
+ import jdk .vm .ci .meta .JavaConstant ;
40
44
import jdk .vm .ci .meta .JavaField ;
41
45
import jdk .vm .ci .meta .JavaKind ;
42
46
import jdk .vm .ci .meta .JavaMethod ;
49
53
/**
50
54
* For certain types, object identity should not be used for object equality check. This phase
51
55
* checks the correct usage of the given type. Equality checks with == or != (except null checks)
52
- * results in an {@link AssertionError}.
56
+ * results in an {@link AssertionError}. Optionally, a singleton value with which == and != checks
57
+ * are allowed as an exception may be provided.
53
58
*/
54
59
public class VerifyUsageWithEquals extends VerifyPhase <CoreProviders > {
55
60
@@ -63,9 +68,34 @@ public boolean checkContract() {
63
68
*/
64
69
private final Class <?> restrictedClass ;
65
70
71
+ /**
72
+ * The value besides {@code null} for which equality checks with == or != are allowed.
73
+ */
74
+ private final Object safeSingletonValue ;
75
+
66
76
public VerifyUsageWithEquals (Class <?> restrictedClass ) {
77
+ checkRestrictedClass (restrictedClass );
78
+ this .restrictedClass = restrictedClass ;
79
+ this .safeSingletonValue = null ;
80
+ }
81
+
82
+ /**
83
+ * Constructs a verifier to check that object identity is not used for equality checks except
84
+ * with {@code null} and the given singleton value.
85
+ *
86
+ * @param restrictedClass the class for which equality checks are restricted
87
+ * @param singletonValue the non-null value for which equality checks with == or != are allowed
88
+ * as an exception
89
+ */
90
+ public <T > VerifyUsageWithEquals (Class <T > restrictedClass , T singletonValue ) {
91
+ checkRestrictedClass (restrictedClass );
67
92
this .restrictedClass = restrictedClass ;
68
- assert !restrictedClass .isInterface () || isTrustedInterface (restrictedClass );
93
+ Objects .requireNonNull (singletonValue );
94
+ this .safeSingletonValue = singletonValue ;
95
+ }
96
+
97
+ private static void checkRestrictedClass (Class <?> restrictedClass ) {
98
+ assert !restrictedClass .isInterface () || isTrustedInterface (restrictedClass ) : "the restricted class must not be an untrusted interface" ;
69
99
}
70
100
71
101
private static final Class <?>[] trustedInterfaceTypes = {JavaType .class , JavaField .class , JavaMethod .class };
@@ -104,10 +134,28 @@ private boolean isAssignableToRestrictedType(ValueNode node, MetaAccessProvider
104
134
return false ;
105
135
}
106
136
137
+ private boolean isSafeConstant (ValueNode node , ConstantReflectionProvider constantReflection , SnippetReflectionProvider snippetReflection ) {
138
+ return isNullConstant (node ) || isSafeSingletonValue (node , constantReflection , snippetReflection );
139
+ }
140
+
107
141
private static boolean isNullConstant (ValueNode node ) {
108
142
return node .isConstant () && node .isNullConstant ();
109
143
}
110
144
145
+ private boolean isSafeSingletonValue (ValueNode node , ConstantReflectionProvider constantReflection , SnippetReflectionProvider snippetReflection ) {
146
+ if (safeSingletonValue == null ) {
147
+ return false ;
148
+ }
149
+ JavaConstant javaConstant = node .asJavaConstant ();
150
+ if (node instanceof LoadFieldNode loadField && loadField .isStatic () && loadField .field ().isFinal ()) {
151
+ javaConstant = constantReflection .readFieldValue (loadField .field (), null );
152
+ }
153
+ if (javaConstant == null ) {
154
+ return false ;
155
+ }
156
+ return safeSingletonValue == snippetReflection .asObject (Object .class , javaConstant );
157
+ }
158
+
111
159
private static boolean isEqualsMethod (ResolvedJavaMethod method ) {
112
160
if (method .getName ().equals ("equals" )) {
113
161
Signature sig = method .getSignature ();
@@ -129,12 +177,14 @@ private static boolean isThisParameter(ValueNode node) {
129
177
}
130
178
131
179
/**
132
- * Checks whether the type of {@code x} is assignable to the restricted type and that {@code y}
133
- * is not a null constant .
180
+ * Checks whether the type of {@code x} or {@code y} is assignable to the restricted type and
181
+ * that {@code x} and {@code y} are not safe constants .
134
182
*/
135
- private boolean isIllegalUsage (ResolvedJavaMethod method , ValueNode x , ValueNode y , MetaAccessProvider metaAccess ) {
136
- if (isAssignableToRestrictedType (x , metaAccess ) && !isNullConstant (y )) {
137
- if (isEqualsMethod (method ) && isThisParameter (x ) || isThisParameter (y )) {
183
+ private boolean isIllegalUsage (ResolvedJavaMethod method , ValueNode x , ValueNode y , CoreProviders context ) {
184
+ if ((isAssignableToRestrictedType (x , context .getMetaAccess ()) || isAssignableToRestrictedType (y , context .getMetaAccess ())) &&
185
+ !isSafeConstant (x , context .getConstantReflection (), context .getSnippetReflection ()) &&
186
+ !isSafeConstant (y , context .getConstantReflection (), context .getSnippetReflection ())) {
187
+ if (isEqualsMethod (method ) && (isThisParameter (x ) || isThisParameter (y ))) {
138
188
return false ;
139
189
}
140
190
return true ;
@@ -151,7 +201,7 @@ protected void verify(StructuredGraph graph, CoreProviders context) {
151
201
152
202
if (restrictedType .isAssignableFrom (method .getDeclaringClass ())) {
153
203
// Allow violation in methods of the restricted type itself and its subclasses.
154
- } else if (isIllegalUsage (method , cn .getX (), cn .getY (), context . getMetaAccess ()) || isIllegalUsage ( method , cn . getY (), cn . getX (), context . getMetaAccess () )) {
204
+ } else if (isIllegalUsage (method , cn .getX (), cn .getY (), context )) {
155
205
throw new VerificationError ("Verification of " + restrictedClass .getName () + " usage failed: Comparing " + cn .getX () + " and " + cn .getY () + " in " + method +
156
206
" must use .equals() for object equality, not '==' or '!='" );
157
207
}
0 commit comments