Skip to content

Commit 4c8a403

Browse files
committed
[GR-24396] Implement and use Interop identity message
PullRequest: graalpython/1072
2 parents 9c88028 + 0ee3487 commit 4c8a403

File tree

4 files changed

+133
-22
lines changed

4 files changed

+133
-22
lines changed

graalpython/com.oracle.graal.python.test/src/tests/test_id.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -70,3 +70,16 @@ def test_id_is_constant_even_when_object_changes():
7070
lid = id(l)
7171
l.append(12)
7272
assert id(l) == lid
73+
74+
def test_identity():
75+
assert True is True
76+
assert memoryview(b"").readonly is True # compare a PInt bool (from C) to a boolean
77+
78+
assert 12 is 12
79+
assert memoryview(b"123").nbytes == 3 # compare PInt (from C) to an int
80+
class I(int): pass
81+
assert I(12) is not 12
82+
assert 12.0 is 12.0
83+
84+
nan = float('nan')
85+
assert nan is nan

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@
158158
import com.oracle.truffle.api.nodes.Node;
159159
import com.oracle.truffle.api.profiles.BranchProfile;
160160
import com.oracle.truffle.api.profiles.ConditionProfile;
161+
import com.oracle.truffle.api.utilities.TriState;
161162

162163
@ImportStatic(SpecialMethodNames.class)
163164
@ExportLibrary(InteropLibrary.class)
@@ -2023,4 +2024,33 @@ public boolean hasMetaObject() {
20232024
public Object getMetaObject(@Shared("getClassThis") @Cached GetClassNode getClass) {
20242025
return getClass.execute(this);
20252026
}
2027+
2028+
@ExportMessage
2029+
public int identityHashCode(
2030+
@CachedLibrary("this") PythonObjectLibrary objectLib) {
2031+
if (objectLib.isHashable(this)) {
2032+
return Long.hashCode(objectLib.hash(this));
2033+
} else {
2034+
// everything in Python has an identity, but not everything provides a __hash__ method
2035+
return systemHashCode(this);
2036+
}
2037+
}
2038+
2039+
@TruffleBoundary
2040+
public static int systemHashCode(Object obj) {
2041+
return System.identityHashCode(obj);
2042+
}
2043+
2044+
@ExportMessage
2045+
public TriState isIdenticalOrUndefined(Object other,
2046+
@CachedLibrary(limit = "3") InteropLibrary otherLib,
2047+
@CachedLibrary("this") PythonObjectLibrary objectLib) {
2048+
if (this == other) {
2049+
return TriState.TRUE;
2050+
} else if (otherLib.hasIdentity(other)) {
2051+
return objectLib.isSame(this, other) ? TriState.TRUE : TriState.FALSE;
2052+
} else {
2053+
return TriState.UNDEFINED;
2054+
}
2055+
}
20262056
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
import com.oracle.graal.python.builtins.PythonBuiltins;
5757
import com.oracle.graal.python.builtins.objects.PNone;
5858
import com.oracle.graal.python.builtins.objects.PNotImplemented;
59+
import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
5960
import com.oracle.graal.python.builtins.objects.cext.CExtNodes;
6061
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
6162
import com.oracle.graal.python.builtins.objects.cext.PythonNativeClass;
@@ -303,9 +304,9 @@ String repr(VirtualFrame frame, Object self,
303304
Object moduleName = readModuleNode.executeObject(frame, type);
304305
Object qualName = readQualNameNode.executeObject(frame, type);
305306
if (moduleName != PNone.NO_VALUE && !BuiltinNames.BUILTINS.equals(moduleName)) {
306-
return strFormat("<%s.%s object at 0x%x>", moduleName, qualName, System.identityHashCode(self));
307+
return strFormat("<%s.%s object at 0x%x>", moduleName, qualName, PythonAbstractObject.systemHashCode(self));
307308
}
308-
return strFormat("<%s object at 0x%x>", qualName, System.identityHashCode(self));
309+
return strFormat("<%s object at 0x%x>", qualName, PythonAbstractObject.systemHashCode(self));
309310
}
310311

311312
@TruffleBoundary

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/expression/IsExpressionNode.java

Lines changed: 86 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,21 @@
4949
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
5050
import com.oracle.graal.python.builtins.objects.code.PCode;
5151
import com.oracle.graal.python.builtins.objects.ints.PInt;
52+
import com.oracle.graal.python.builtins.objects.object.PythonObjectLibrary;
5253
import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass;
5354
import com.oracle.graal.python.nodes.expression.IsExpressionNodeGen.IsNodeGen;
55+
import com.oracle.graal.python.nodes.object.IsBuiltinClassProfile;
5456
import com.oracle.graal.python.runtime.PythonContext;
57+
import com.oracle.graal.python.runtime.PythonCore;
5558
import com.oracle.graal.python.runtime.PythonOptions;
5659
import com.oracle.truffle.api.RootCallTarget;
5760
import com.oracle.truffle.api.dsl.Cached;
61+
import com.oracle.truffle.api.dsl.Cached.Shared;
5862
import com.oracle.truffle.api.dsl.CachedContext;
59-
import com.oracle.truffle.api.dsl.Fallback;
6063
import com.oracle.truffle.api.dsl.GenerateUncached;
64+
import com.oracle.truffle.api.dsl.ImportStatic;
6165
import com.oracle.truffle.api.dsl.Specialization;
66+
import com.oracle.truffle.api.library.CachedLibrary;
6267
import com.oracle.truffle.api.nodes.Node;
6368

6469
public abstract class IsExpressionNode extends BinaryOpNode {
@@ -72,6 +77,7 @@ boolean doIt(Object left, Object right,
7277
return isNode.execute(left, right);
7378
}
7479

80+
@ImportStatic(PythonOptions.class)
7581
@GenerateUncached
7682
public abstract static class IsNode extends Node {
7783

@@ -81,6 +87,7 @@ public final boolean execute(Object left, Object right) {
8187
return left == right || executeInternal(left, right);
8288
}
8389

90+
// Primitives
8491
@Specialization
8592
boolean doBB(boolean left, boolean right) {
8693
return left == right;
@@ -104,6 +111,17 @@ boolean doBD(boolean left, double right) {
104111
return false;
105112
}
106113

114+
@Specialization
115+
boolean doBP(boolean left, PInt right,
116+
@Shared("ctxt") @CachedContext(PythonLanguage.class) PythonContext ctxt) {
117+
PythonCore core = ctxt.getCore();
118+
if (left) {
119+
return right == core.getTrue();
120+
} else {
121+
return right == core.getFalse();
122+
}
123+
}
124+
107125
@SuppressWarnings("unused")
108126
@Specialization
109127
boolean doIB(int left, boolean right) {
@@ -127,10 +145,15 @@ boolean doID(int left, double right) {
127145
}
128146

129147
@Specialization
130-
boolean doIP(int left, PInt right) {
131-
try {
132-
return right.intValueExact() == left;
133-
} catch (ArithmeticException e) {
148+
boolean doIP(int left, PInt right,
149+
@Shared("isBuiltin") @Cached IsBuiltinClassProfile isBuiltin) {
150+
if (isBuiltin.profileIsAnyBuiltinObject(right)) {
151+
try {
152+
return right.intValueExact() == left;
153+
} catch (ArithmeticException e) {
154+
return false;
155+
}
156+
} else {
134157
return false;
135158
}
136159
}
@@ -158,10 +181,15 @@ boolean doLD(long left, double right) {
158181
}
159182

160183
@Specialization
161-
boolean doLP(long left, PInt right) {
162-
try {
163-
return left == right.longValueExact();
164-
} catch (ArithmeticException e) {
184+
boolean doLP(long left, PInt right,
185+
@Shared("isBuiltin") @Cached IsBuiltinClassProfile isBuiltin) {
186+
if (isBuiltin.profileIsAnyBuiltinObject(right)) {
187+
try {
188+
return left == right.longValueExact();
189+
} catch (ArithmeticException e) {
190+
return false;
191+
}
192+
} else {
165193
return false;
166194
}
167195
}
@@ -191,6 +219,25 @@ boolean doDD(double left, double right) {
191219
return left == right || (Double.isNaN(left) && Double.isNaN(right));
192220
}
193221

222+
@Specialization
223+
boolean doPB(PInt left, boolean right,
224+
@Shared("ctxt") @CachedContext(PythonLanguage.class) PythonContext ctxt) {
225+
return doBP(right, left, ctxt);
226+
}
227+
228+
@Specialization
229+
boolean doPI(PInt left, int right,
230+
@Shared("isBuiltin") @Cached IsBuiltinClassProfile isBuiltin) {
231+
return doIP(right, left, isBuiltin);
232+
}
233+
234+
@Specialization
235+
boolean doPL(PInt left, long right,
236+
@Shared("isBuiltin") @Cached IsBuiltinClassProfile isBuiltin) {
237+
return doLP(right, left, isBuiltin);
238+
}
239+
240+
// types
194241
@Specialization
195242
boolean doCT(PythonBuiltinClass left, PythonBuiltinClassType right) {
196243
return left.getType() == right;
@@ -201,33 +248,40 @@ boolean doTC(PythonBuiltinClassType left, PythonBuiltinClass right) {
201248
return right.getType() == left;
202249
}
203250

251+
// native objects
204252
@Specialization
205253
boolean doNative(PythonAbstractNativeObject left, PythonAbstractNativeObject right,
206254
@Cached CExtNodes.PointerCompareNode isNode) {
207255
return isNode.execute(__EQ__, left, right);
208256
}
209257

258+
// code
210259
@Specialization
211260
boolean doCode(PCode left, PCode right) {
212261
// Special case for code objects: Frames create them on-demand even if they refer to the
213262
// same function. So we need to compare the root nodes.
214-
RootCallTarget leftCt = left.getRootCallTarget();
215-
RootCallTarget rightCt = right.getRootCallTarget();
216-
if (leftCt != null && rightCt != null) {
217-
// TODO: handle splitting, i.e., cloned root nodes
218-
return leftCt.getRootNode() == rightCt.getRootNode();
263+
if (left != right) {
264+
RootCallTarget leftCt = left.getRootCallTarget();
265+
RootCallTarget rightCt = right.getRootCallTarget();
266+
if (leftCt != null && rightCt != null) {
267+
// TODO: handle splitting, i.e., cloned root nodes
268+
return leftCt.getRootNode() == rightCt.getRootNode();
269+
} else {
270+
return false;
271+
}
219272
}
220-
return left == right;
273+
return true;
221274
}
222275

276+
// none
223277
@Specialization
224278
boolean doObjectPNone(Object left, PNone right,
225279
@Cached.Shared("ctxt") @CachedContext(PythonLanguage.class) PythonContext ctxt) {
226280
if (ctxt.getOption(PythonOptions.EmulateJython) && ctxt.getEnv().isHostObject(left) && ctxt.getEnv().asHostObject(left) == null &&
227281
right == PNone.NONE) {
228282
return true;
229283
}
230-
return doGeneric(left, right);
284+
return left == right;
231285
}
232286

233287
@Specialization
@@ -236,9 +290,22 @@ boolean doPNoneObject(PNone left, Object right,
236290
return doObjectPNone(right, left, ctxt);
237291
}
238292

239-
@Fallback
240-
boolean doGeneric(Object left, Object right) {
241-
return left == right;
293+
// everything else
294+
@Specialization(limit = "getCallSiteInlineCacheMaxDepth()")
295+
boolean doGeneric(Object left, Object right,
296+
@CachedLibrary("left") PythonObjectLibrary lib) {
297+
if (left == right) {
298+
return true;
299+
}
300+
if (lib.isForeignObject(left) || lib.isReflectedObject(left, left)) {
301+
// If left is foreign, this will check its identity via the interop message. If left
302+
// is an object that is a wrapped Python object and uses a ReflectionLibrary, it
303+
// will not appear foreign, but the isSame call will unpack it from its wrapper and
304+
// may lead straight back to this node, but this time with the unwrapped Python
305+
// object that will no longer satisfy the isReflectedObject condition.
306+
return lib.isSame(left, right);
307+
}
308+
return false;
242309
}
243310

244311
public static IsNode create() {

0 commit comments

Comments
 (0)