Skip to content

Commit 126d2c6

Browse files
committed
[GR-20141] Add hash message to PythonObjectLibrary
PullRequest: graalpython/751
2 parents cd5f4ad + 6bcd1b2 commit 126d2c6

24 files changed

+412
-296
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,13 @@ static boolean isMappingType(PythonBuiltinClassType type,
458458
return lib.isMappingType(context.getCore().lookupType(type));
459459
}
460460

461+
@ExportMessage
462+
static long hash(PythonBuiltinClassType type,
463+
@CachedContext(PythonLanguage.class) PythonContext context,
464+
@CachedLibrary(limit = "1") PythonObjectLibrary lib) {
465+
return lib.hash(context.getCore().lookupType(type));
466+
}
467+
461468
@ExportMessage
462469
static LazyPythonClass getLazyPythonClass(@SuppressWarnings("unused") PythonBuiltinClassType type) {
463470
return PythonClass;

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java

Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@
139139
import com.oracle.graal.python.nodes.call.CallNode;
140140
import com.oracle.graal.python.nodes.call.GenericInvokeNode;
141141
import com.oracle.graal.python.nodes.call.PythonCallNode;
142-
import com.oracle.graal.python.nodes.call.special.CallUnaryMethodNode;
143142
import com.oracle.graal.python.nodes.call.special.LookupAndCallBinaryNode;
144143
import com.oracle.graal.python.nodes.call.special.LookupAndCallTernaryNode;
145144
import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode;
@@ -490,39 +489,19 @@ public char charFromObject(Object arg) {
490489
}
491490
}
492491

493-
// hash([object])
492+
// hash(object)
494493
@Builtin(name = HASH, minNumOfPositionalArgs = 1)
495494
@GenerateNodeFactory
496495
public abstract static class HashNode extends PythonUnaryBuiltinNode {
497-
@Specialization // tfel: TODO: this shouldn't be needed!
498-
Object hash(PException exception) {
499-
return exception.hashCode();
500-
}
501-
502-
protected boolean isPException(Object object) {
503-
return object instanceof PException;
504-
}
505-
506-
@Specialization(guards = "!isPException(object)")
507-
Object hash(VirtualFrame frame, Object object,
508-
@Cached("create(__DIR__)") LookupInheritedAttributeNode lookupDirNode,
509-
@Cached("create(__HASH__)") LookupInheritedAttributeNode lookupHash,
510-
@CachedLibrary(limit = "1") PythonObjectLibrary dataModelLibrary,
511-
@Cached CallUnaryMethodNode callUnary,
512-
@Cached("createIfTrueNode()") CastToBooleanNode trueNode,
513-
@Cached IsInstanceNode isInstanceNode) {
514-
if (trueNode.executeBoolean(frame, lookupDirNode.execute(object))) {
515-
Object hashAttr = lookupHash.execute(object);
516-
if (!dataModelLibrary.isCallable(hashAttr)) {
517-
throw raise(PythonErrorType.TypeError, "unhashable type: '%p'", object);
518-
}
519-
Object hashValue = callUnary.executeObject(frame, hashAttr, object);
520-
if (isInstanceNode.executeWith(frame, hashValue, getBuiltinPythonClass(PythonBuiltinClassType.PInt))) {
521-
return hashValue;
522-
}
523-
throw raise(PythonErrorType.TypeError, "__hash__ method should return an integer");
496+
@Specialization(limit = "getCallSiteInlineCacheMaxDepth()")
497+
long hash(VirtualFrame frame, Object object,
498+
@Cached("createBinaryProfile()") ConditionProfile profile,
499+
@CachedLibrary("object") PythonObjectLibrary lib) {
500+
if (profile.profile(frame != null)) {
501+
return lib.hashWithState(object, PArguments.getThreadState(frame));
502+
} else {
503+
return lib.hash(object);
524504
}
525-
return object.hashCode();
526505
}
527506
}
528507

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixModuleBuiltins.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,9 @@
131131
import com.oracle.graal.python.nodes.util.CastToIndexNode;
132132
import com.oracle.graal.python.nodes.util.CastToIntegerFromIntNode;
133133
import com.oracle.graal.python.nodes.util.CastToJavaIntNode;
134-
import com.oracle.graal.python.nodes.util.CastToJavaLongNode;
135134
import com.oracle.graal.python.nodes.util.CastToPathNode;
136135
import com.oracle.graal.python.nodes.util.ChannelNodes.ReadFromChannelNode;
136+
import com.oracle.graal.python.nodes.util.CoerceToJavaLongNode;
137137
import com.oracle.graal.python.runtime.PosixResources;
138138
import com.oracle.graal.python.runtime.PythonContext;
139139
import com.oracle.graal.python.runtime.PythonCore;
@@ -1037,8 +1037,8 @@ Object lseek(VirtualFrame frame, long fd, long pos, int how,
10371037
@Specialization
10381038
Object lseekGeneric(VirtualFrame frame, Object fd, Object pos, Object how,
10391039
@Shared("channelClassProfile") @Cached("createClassProfile()") ValueProfile channelClassProfile,
1040-
@Cached CastToJavaLongNode castFdNode,
1041-
@Cached CastToJavaLongNode castPosNode,
1040+
@Cached CoerceToJavaLongNode castFdNode,
1041+
@Cached CoerceToJavaLongNode castPosNode,
10421042
@Cached CastToJavaIntNode castHowNode) {
10431043

10441044
return lseek(frame, castFdNode.execute(fd), castPosNode.execute(pos), castHowNode.execute(how), channelClassProfile);
@@ -1248,15 +1248,15 @@ Object readLong(@SuppressWarnings("unused") VirtualFrame frame, int fd, long req
12481248
Object read(@SuppressWarnings("unused") VirtualFrame frame, int fd, Object requestedSize,
12491249
@Shared("profile") @Cached("createClassProfile()") ValueProfile channelClassProfile,
12501250
@Shared("readNode") @Cached ReadFromChannelNode readNode,
1251-
@Cached CastToJavaLongNode castToLongNode) {
1251+
@Cached CoerceToJavaLongNode castToLongNode) {
12521252
return readLong(frame, fd, castToLongNode.execute(requestedSize), channelClassProfile, readNode);
12531253
}
12541254

12551255
@Specialization
12561256
Object readFdGeneric(@SuppressWarnings("unused") VirtualFrame frame, Object fd, Object requestedSize,
12571257
@Shared("profile") @Cached("createClassProfile()") ValueProfile channelClassProfile,
12581258
@Shared("readNode") @Cached ReadFromChannelNode readNode,
1259-
@Cached CastToJavaLongNode castToLongNode,
1259+
@Cached CoerceToJavaLongNode castToLongNode,
12601260
@Cached CastToJavaIntNode castToIntNode) {
12611261
return readLong(frame, castToIntNode.execute(fd), castToLongNode.execute(requestedSize), channelClassProfile, readNode);
12621262
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/PythonAbstractObject.java

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
*/
4141
package com.oracle.graal.python.builtins.objects;
4242

43-
import com.oracle.graal.python.PythonLanguage;
4443
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.AttributeError;
4544
import static com.oracle.graal.python.nodes.SpecialMethodNames.__CALL__;
4645
import static com.oracle.graal.python.nodes.SpecialMethodNames.__DELETE__;
@@ -58,13 +57,20 @@
5857
import static com.oracle.graal.python.nodes.SpecialMethodNames.__SETITEM__;
5958
import static com.oracle.graal.python.nodes.SpecialMethodNames.__SET__;
6059

60+
import java.time.LocalDate;
61+
import java.time.LocalTime;
62+
import java.time.ZoneId;
63+
import java.time.ZoneOffset;
6164
import java.util.HashSet;
6265

66+
import com.oracle.graal.python.PythonLanguage;
6367
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
6468
import com.oracle.graal.python.builtins.objects.bytes.PBytes;
6569
import com.oracle.graal.python.builtins.objects.cext.DynamicObjectNativeWrapper;
6670
import com.oracle.graal.python.builtins.objects.common.SequenceNodes;
6771
import com.oracle.graal.python.builtins.objects.dict.PDict;
72+
import com.oracle.graal.python.builtins.objects.function.PArguments;
73+
import com.oracle.graal.python.builtins.objects.function.PArguments.ThreadState;
6874
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
6975
import com.oracle.graal.python.builtins.objects.function.PFunction;
7076
import com.oracle.graal.python.builtins.objects.function.PKeyword;
@@ -102,6 +108,7 @@
102108
import com.oracle.graal.python.nodes.object.GetLazyClassNode;
103109
import com.oracle.graal.python.nodes.object.IsBuiltinClassProfile;
104110
import com.oracle.graal.python.nodes.util.CastToJavaIntNode;
111+
import com.oracle.graal.python.nodes.util.CastToJavaLongNode;
105112
import com.oracle.graal.python.runtime.exception.PException;
106113
import com.oracle.truffle.api.CompilerDirectives;
107114
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -124,10 +131,6 @@
124131
import com.oracle.truffle.api.nodes.ExplodeLoop;
125132
import com.oracle.truffle.api.nodes.Node;
126133
import com.oracle.truffle.api.profiles.ConditionProfile;
127-
import java.time.LocalDate;
128-
import java.time.LocalTime;
129-
import java.time.ZoneId;
130-
import java.time.ZoneOffset;
131134

132135
@ExportLibrary(InteropLibrary.class)
133136
@ExportLibrary(PythonObjectLibrary.class)
@@ -667,6 +670,38 @@ public final boolean isHashable(@Exclusive @Cached LookupInheritedAttributeNode.
667670
return dataModelLibrary.isCallable(hashAttr);
668671
}
669672

673+
@ExportMessage
674+
public long hashWithState(ThreadState state,
675+
@Exclusive @Cached("createBinaryProfile()") ConditionProfile gotState,
676+
@Exclusive @Cached LookupInheritedAttributeNode.Dynamic lookupHashAttributeNode,
677+
@Exclusive @Cached CallNode callNode,
678+
@Exclusive @Cached PRaiseNode raise,
679+
@Cached CastToJavaLongNode castToLong,
680+
@CachedLibrary(limit = "1") PythonObjectLibrary lib) {
681+
Object hashAttr = getHashAttr(lookupHashAttributeNode, raise, lib);
682+
Object result;
683+
if (gotState.profile(state == null)) {
684+
result = callNode.execute(hashAttr, this);
685+
} else {
686+
result = callNode.execute(PArguments.frameForCall(state), hashAttr, this);
687+
}
688+
// see PyObject_GetHash and slot_tp_hash in CPython. The result of the
689+
// hash call is always a plain long, forcibly and lossy read from memory.
690+
try {
691+
return castToLong.execute(result);
692+
} catch (CastToJavaLongNode.CannotCastException e) {
693+
throw raise.raise(PythonBuiltinClassType.TypeError, "__hash__ method should return an integer");
694+
}
695+
}
696+
697+
private Object getHashAttr(LookupInheritedAttributeNode.Dynamic lookupHashAttributeNode, PRaiseNode raise, PythonObjectLibrary lib) {
698+
Object hashAttr = lookupHashAttributeNode.execute(this, __HASH__);
699+
if (!lib.isCallable(hashAttr)) {
700+
throw raise.raise(PythonBuiltinClassType.TypeError, "unhashable type: '%p'", this);
701+
}
702+
return hashAttr;
703+
}
704+
670705
@ExportMessage
671706
public final boolean canBeIndex(@Exclusive @Cached HasInheritedAttributeNode.Dynamic hasIndexAttribute) {
672707
return hasIndexAttribute.execute(this, __INDEX__);

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/PySequenceArrayWrapper.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
import com.oracle.graal.python.nodes.call.CallNode;
6464
import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode.LookupAndCallUnaryDynamicNode;
6565
import com.oracle.graal.python.nodes.truffle.PythonTypes;
66-
import com.oracle.graal.python.nodes.util.CastToJavaLongNode;
66+
import com.oracle.graal.python.nodes.util.CoerceToJavaLongNode;
6767
import com.oracle.graal.python.runtime.sequence.PSequence;
6868
import com.oracle.graal.python.runtime.sequence.storage.EmptySequenceStorage;
6969
import com.oracle.graal.python.runtime.sequence.storage.NativeSequenceStorage;
@@ -139,7 +139,7 @@ public boolean equals(Object obj) {
139139
final long getArraySize(
140140
@CachedLibrary("this") PythonNativeWrapperLibrary lib,
141141
@Shared("callLenNode") @Cached LookupAndCallUnaryDynamicNode callLenNode,
142-
@Shared("castToLongNode") @Cached CastToJavaLongNode castToLongNode) {
142+
@Shared("castToLongNode") @Cached CoerceToJavaLongNode castToLongNode) {
143143
return castToLongNode.execute(callLenNode.executeObject(lib.getDelegate(this), SpecialMethodNames.__LEN__));
144144
}
145145

@@ -160,7 +160,7 @@ final Object readArrayElement(long index,
160160
final boolean isArrayElementReadable(long identifier,
161161
@CachedLibrary("this") PythonNativeWrapperLibrary lib,
162162
@Shared("callLenNode") @Cached LookupAndCallUnaryDynamicNode callLenNode,
163-
@Shared("castToLongNode") @Cached CastToJavaLongNode castToLongNode) {
163+
@Shared("castToLongNode") @Cached CoerceToJavaLongNode castToLongNode) {
164164
// also include the implicit null-terminator
165165
return 0 <= identifier && identifier <= getArraySize(lib, callLenNode, castToLongNode);
166166
}
@@ -196,7 +196,7 @@ long doBytesI64(PIBytesLike bytesLike, long byteIdx,
196196
@Cached("createClassProfile()") ValueProfile profile,
197197
@Cached SequenceStorageNodes.LenNode lenNode,
198198
@Cached SequenceStorageNodes.GetItemDynamicNode getItemNode,
199-
@Shared("castToLongNode") @Cached CastToJavaLongNode castToJavaLongNode) {
199+
@Shared("castToLongNode") @Cached CoerceToJavaLongNode castToJavaLongNode) {
200200
PIBytesLike profiled = profile.profile(bytesLike);
201201
int len = lenNode.execute(profiled.getSequenceStorage());
202202
// simulate sentinel value
@@ -236,7 +236,7 @@ long doBytesI64(PIBytesLike bytesLike, long byteIdx,
236236
long doPMmapI64(PMMap mmap, long byteIdx,
237237
@Exclusive @Cached LookupInheritedAttributeNode.Dynamic lookupGetItemNode,
238238
@Exclusive @Cached CallNode callGetItemNode,
239-
@Shared("castToLongNode") @Cached CastToJavaLongNode castToJavaLongNode) {
239+
@Shared("castToLongNode") @Cached CoerceToJavaLongNode castToJavaLongNode) {
240240

241241
long len = mmap.getLength();
242242
Object attrGetItem = lookupGetItemNode.execute(mmap, SpecialMethodNames.__GETITEM__);
@@ -303,23 +303,23 @@ public void removeArrayElement(@SuppressWarnings("unused") long index) throws Un
303303
public boolean isArrayElementModifiable(long index,
304304
@CachedLibrary("this") PythonNativeWrapperLibrary lib,
305305
@Shared("callLenNode") @Cached LookupAndCallUnaryDynamicNode callLenNode,
306-
@Shared("castToLongNode") @Cached CastToJavaLongNode castToLongNode) {
306+
@Shared("castToLongNode") @Cached CoerceToJavaLongNode castToLongNode) {
307307
return 0 <= index && index <= getArraySize(lib, callLenNode, castToLongNode);
308308
}
309309

310310
@ExportMessage
311311
public boolean isArrayElementInsertable(long index,
312312
@CachedLibrary("this") PythonNativeWrapperLibrary lib,
313313
@Shared("callLenNode") @Cached LookupAndCallUnaryDynamicNode callLenNode,
314-
@Shared("castToLongNode") @Cached CastToJavaLongNode castToLongNode) {
314+
@Shared("castToLongNode") @Cached CoerceToJavaLongNode castToLongNode) {
315315
return 0 <= index && index <= getArraySize(lib, callLenNode, castToLongNode);
316316
}
317317

318318
@ExportMessage
319319
public boolean isArrayElementRemovable(long index,
320320
@CachedLibrary("this") PythonNativeWrapperLibrary lib,
321321
@Shared("callLenNode") @Cached LookupAndCallUnaryDynamicNode callLenNode,
322-
@Shared("castToLongNode") @Cached CastToJavaLongNode castToLongNode) {
322+
@Shared("castToLongNode") @Cached CoerceToJavaLongNode castToLongNode) {
323323
return 0 <= index && index <= getArraySize(lib, callLenNode, castToLongNode);
324324
}
325325

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/PythonAbstractNativeObject.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ public void setDict(PHashingCollection value) throws UnsupportedMessageException
162162
public abstract static class GetDict {
163163
@Specialization
164164
public static PHashingCollection getNativeDictionary(PythonAbstractNativeObject self,
165-
@Cached PRaiseNode raiseNode,
165+
@Exclusive @Cached PRaiseNode raiseNode,
166166
@Exclusive @Cached ToSulongNode toSulong,
167167
@Exclusive @Cached ToJavaNode toJava,
168168
@CachedLibrary(limit = "1") InteropLibrary interopLibrary,

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

Lines changed: 4 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,12 @@
4141
package com.oracle.graal.python.builtins.objects.common;
4242

4343
import static com.oracle.graal.python.nodes.SpecialMethodNames.__EQ__;
44-
import static com.oracle.graal.python.nodes.SpecialMethodNames.__HASH__;
45-
import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError;
4644

4745
import java.util.Iterator;
4846

4947
import com.oracle.graal.python.PythonLanguage;
48+
import com.oracle.graal.python.builtins.objects.object.PythonObjectLibrary;
5049
import com.oracle.graal.python.nodes.PNodeWithContext;
51-
import com.oracle.graal.python.nodes.PRaiseNode;
52-
import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode;
5350
import com.oracle.graal.python.nodes.expression.BinaryComparisonNode;
5451
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
5552
import com.oracle.truffle.api.CompilerDirectives.ValueType;
@@ -98,7 +95,7 @@ public abstract static class Equivalence extends PNodeWithContext {
9895

9996
@Override
10097
public int hashCode(Object o) {
101-
return o.hashCode();
98+
return Long.hashCode(o.hashCode());
10299
}
103100

104101
@Override
@@ -112,31 +109,6 @@ private boolean objectEquals(Object a, Object b) {
112109
}
113110
};
114111

115-
public static class HashRootNode extends RootNode {
116-
@Child private LookupAndCallUnaryNode callHashNode = LookupAndCallUnaryNode.create(__HASH__);
117-
118-
protected HashRootNode() {
119-
super(PythonLanguage.getCurrent());
120-
Truffle.getRuntime().createCallTarget(this);
121-
}
122-
123-
@Override
124-
public Object execute(VirtualFrame frame) {
125-
Object[] args = frame.getArguments();
126-
return callHashNode.executeObject(frame, args[0]);
127-
}
128-
129-
@Override
130-
public SourceSection getSourceSection() {
131-
return null;
132-
}
133-
134-
@Override
135-
public boolean isCloningAllowed() {
136-
return true;
137-
}
138-
}
139-
140112
private static class EqualsRootNode extends RootNode {
141113
@Child private BinaryComparisonNode callEqNode = BinaryComparisonNode.create(__EQ__, __EQ__, "==");
142114

@@ -163,20 +135,12 @@ public boolean isCloningAllowed() {
163135
}
164136

165137
public static class SlowPathEquivalence extends Equivalence {
166-
167-
private final HashRootNode hashRootNode = new HashRootNode();
168138
private final EqualsRootNode eqRootNode = new EqualsRootNode();
169139

170140
@Override
171141
public int hashCode(Object o) {
172-
Object result = hashRootNode.getCallTarget().call(o);
173-
if (result instanceof Integer) {
174-
return (int) result;
175-
} else if (result instanceof Long) {
176-
return ((Long) result).intValue();
177-
} else {
178-
throw PRaiseNode.getUncached().raise(TypeError, "__hash__ method should return an integer");
179-
}
142+
long hash = PythonObjectLibrary.getUncached().hash(o);
143+
return Long.hashCode(hash);
180144
}
181145

182146
@Override

0 commit comments

Comments
 (0)