Skip to content

Commit c747dbe

Browse files
committed
create IsHashableNode - exceptions raised in the __hash__ method must pass through
1 parent 6a49fc7 commit c747dbe

File tree

2 files changed

+87
-22
lines changed

2 files changed

+87
-22
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/HashingStorageNodes.java

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode;
7979
import com.oracle.graal.python.nodes.control.GetIteratorNode;
8080
import com.oracle.graal.python.nodes.control.GetNextNode;
81+
import com.oracle.graal.python.nodes.datamodel.IsHashableNode;
8182
import com.oracle.graal.python.nodes.expression.BinaryComparisonNode;
8283
import com.oracle.graal.python.nodes.object.GetClassNode;
8384
import com.oracle.graal.python.runtime.exception.PException;
@@ -170,7 +171,7 @@ public static PythonEquivalence create() {
170171
@ImportStatic(PGuards.class)
171172
abstract static class DictStorageBaseNode extends PBaseNode {
172173
@Child private GetClassNode getClassNode;
173-
@Child private LookupAndCallUnaryNode lookupHashAttributeNode;
174+
@Child private IsHashableNode isHashableNode;
174175
@Child private Equivalence equivalenceNode;
175176

176177
protected Equivalence getEquivalence() {
@@ -189,8 +190,6 @@ protected PythonClass getClass(Object object) {
189190
return getClassNode.execute(object);
190191
}
191192

192-
private final ValueProfile keyTypeProfile = ValueProfile.createClassProfile();
193-
194193
protected boolean isJavaString(Object o) {
195194
return o instanceof String || o instanceof PString && wrappedString((PString) o);
196195
}
@@ -205,27 +204,11 @@ protected static Shape lookupShape(DynamicObject receiver) {
205204
}
206205

207206
protected boolean isHashable(Object key) {
208-
Object profiledKey = keyTypeProfile.profile(key);
209-
if (PGuards.isString(profiledKey)) {
210-
return true;
211-
} else if (PGuards.isInteger(profiledKey)) {
212-
return true;
213-
} else if (profiledKey instanceof Double || PGuards.isPFloat(profiledKey)) {
214-
return true;
215-
}
216-
217-
if (lookupHashAttributeNode == null) {
207+
if (isHashableNode == null) {
218208
CompilerDirectives.transferToInterpreterAndInvalidate();
219-
lookupHashAttributeNode = insert(LookupAndCallUnaryNode.create(__HASH__));
209+
isHashableNode = insert(IsHashableNode.create());
220210
}
221-
222-
try {
223-
lookupHashAttributeNode.executeObject(key);
224-
return true;
225-
} catch (PException e) {
226-
// ignore
227-
}
228-
return false;
211+
return isHashableNode.execute(key);
229212
}
230213

231214
protected PException unhashable(Object key) {
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright (c) 2018, Oracle and/or its affiliates.
3+
*
4+
* The Universal Permissive License (UPL), Version 1.0
5+
*
6+
* Subject to the condition set forth below, permission is hereby granted to any
7+
* person obtaining a copy of this software, associated documentation and/or data
8+
* (collectively the "Software"), free of charge and under any and all copyright
9+
* rights in the Software, and any and all patent rights owned or freely
10+
* licensable by each licensor hereunder covering either (i) the unmodified
11+
* Software as contributed to or provided by such licensor, or (ii) the Larger
12+
* Works (as defined below), to deal in both
13+
*
14+
* (a) the Software, and
15+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
16+
* one is included with the Software (each a "Larger Work" to which the
17+
* Software is contributed by such licensors),
18+
*
19+
* without restriction, including without limitation the rights to copy, create
20+
* derivative works of, display, perform, and distribute the Software and make,
21+
* use, sell, offer for sale, import, export, have made, and have sold the
22+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
23+
* either these or other terms.
24+
*
25+
* This license is subject to the following condition:
26+
*
27+
* The above copyright notice and either this complete permission notice or at a
28+
* minimum a reference to the UPL must be included in all copies or substantial
29+
* portions of the Software.
30+
*
31+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
34+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
35+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
36+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
37+
* SOFTWARE.
38+
*/
39+
package com.oracle.graal.python.nodes.datamodel;
40+
41+
import com.oracle.graal.python.nodes.PGuards;
42+
import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode;
43+
import com.oracle.graal.python.runtime.exception.PythonErrorType;
44+
import com.oracle.truffle.api.dsl.Cached;
45+
import com.oracle.truffle.api.dsl.Specialization;
46+
import com.oracle.truffle.api.profiles.ConditionProfile;
47+
48+
public abstract class IsHashableNode extends PDataModelEmulationNode {
49+
protected boolean isDouble(Object object) {
50+
return object instanceof Double || PGuards.isPFloat(object);
51+
}
52+
53+
@Specialization(guards = "isString(object)")
54+
protected boolean isHashableString(@SuppressWarnings("unused") Object object) {
55+
return true;
56+
}
57+
58+
@Specialization(guards = "isInteger(object)")
59+
protected boolean isHashableInt(@SuppressWarnings("unused") Object object) {
60+
return true;
61+
}
62+
63+
@Specialization(guards = "isDouble(object)")
64+
protected boolean isHashableGeneric(@SuppressWarnings("unused") Object object) {
65+
return true;
66+
}
67+
68+
@Specialization
69+
protected boolean isHashableGeneric(Object object,
70+
@Cached("create(__HASH__)") LookupAndCallUnaryNode lookupHashAttributeNode,
71+
@Cached("createBinaryProfile()") ConditionProfile isIntHashProfile) {
72+
Object hashValue = lookupHashAttributeNode.executeObject(object);
73+
if (isIntHashProfile.profile(hashValue instanceof Integer)) {
74+
return true;
75+
}
76+
throw raise(PythonErrorType.TypeError, "__hash__ method should return an integer");
77+
}
78+
79+
public static IsHashableNode create() {
80+
return IsHashableNodeGen.create();
81+
}
82+
}

0 commit comments

Comments
 (0)