Skip to content

Commit ecb876c

Browse files
committed
implement and use Interop identity message
1 parent da33164 commit ecb876c

File tree

3 files changed

+114
-19
lines changed

3 files changed

+114
-19
lines changed

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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: 17 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,20 @@ 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(@CachedLibrary("this") PythonObjectLibrary objectLib) {
2030+
return Long.hashCode(objectLib.hash(this));
2031+
}
2032+
2033+
@ExportMessage
2034+
public TriState isIdenticalOrUndefined(Object other,
2035+
@CachedLibrary(limit = "3") InteropLibrary otherLib,
2036+
@CachedLibrary("this") PythonObjectLibrary objectLib) {
2037+
if (otherLib.hasIdentity(other)) {
2038+
return objectLib.isSame(this, other) ? TriState.TRUE : TriState.FALSE;
2039+
} else {
2040+
return TriState.UNDEFINED;
2041+
}
2042+
}
20262043
}

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

Lines changed: 84 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,20 @@
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;
5557
import com.oracle.graal.python.runtime.PythonOptions;
5658
import com.oracle.truffle.api.RootCallTarget;
5759
import com.oracle.truffle.api.dsl.Cached;
60+
import com.oracle.truffle.api.dsl.Cached.Shared;
5861
import com.oracle.truffle.api.dsl.CachedContext;
59-
import com.oracle.truffle.api.dsl.Fallback;
6062
import com.oracle.truffle.api.dsl.GenerateUncached;
63+
import com.oracle.truffle.api.dsl.ImportStatic;
6164
import com.oracle.truffle.api.dsl.Specialization;
65+
import com.oracle.truffle.api.library.CachedLibrary;
6266
import com.oracle.truffle.api.nodes.Node;
6367

6468
public abstract class IsExpressionNode extends BinaryOpNode {
@@ -72,6 +76,7 @@ boolean doIt(Object left, Object right,
7276
return isNode.execute(left, right);
7377
}
7478

79+
@ImportStatic(PythonOptions.class)
7580
@GenerateUncached
7681
public abstract static class IsNode extends Node {
7782

@@ -81,6 +86,7 @@ public final boolean execute(Object left, Object right) {
8186
return left == right || executeInternal(left, right);
8287
}
8388

89+
// Primitives
8490
@Specialization
8591
boolean doBB(boolean left, boolean right) {
8692
return left == right;
@@ -104,6 +110,16 @@ boolean doBD(boolean left, double right) {
104110
return false;
105111
}
106112

113+
@Specialization
114+
boolean doBP(boolean left, PInt right,
115+
@Shared("ctxt") @CachedContext(PythonLanguage.class) PythonContext ctxt) {
116+
if (left) {
117+
return right == ctxt.getCore().getTrue();
118+
} else {
119+
return right == ctxt.getCore().getFalse();
120+
}
121+
}
122+
107123
@SuppressWarnings("unused")
108124
@Specialization
109125
boolean doIB(int left, boolean right) {
@@ -127,10 +143,15 @@ boolean doID(int left, double right) {
127143
}
128144

129145
@Specialization
130-
boolean doIP(int left, PInt right) {
131-
try {
132-
return right.intValueExact() == left;
133-
} catch (ArithmeticException e) {
146+
boolean doIP(int left, PInt right,
147+
@Shared("isBuiltin") @Cached IsBuiltinClassProfile isBuiltin) {
148+
if (isBuiltin.profileIsAnyBuiltinObject(right)) {
149+
try {
150+
return right.intValueExact() == left;
151+
} catch (ArithmeticException e) {
152+
return false;
153+
}
154+
} else {
134155
return false;
135156
}
136157
}
@@ -158,10 +179,15 @@ boolean doLD(long left, double right) {
158179
}
159180

160181
@Specialization
161-
boolean doLP(long left, PInt right) {
162-
try {
163-
return left == right.longValueExact();
164-
} catch (ArithmeticException e) {
182+
boolean doLP(long left, PInt right,
183+
@Shared("isBuiltin") @Cached IsBuiltinClassProfile isBuiltin) {
184+
if (isBuiltin.profileIsAnyBuiltinObject(right)) {
185+
try {
186+
return left == right.longValueExact();
187+
} catch (ArithmeticException e) {
188+
return false;
189+
}
190+
} else {
165191
return false;
166192
}
167193
}
@@ -191,6 +217,25 @@ boolean doDD(double left, double right) {
191217
return left == right || (Double.isNaN(left) && Double.isNaN(right));
192218
}
193219

220+
@Specialization
221+
boolean doPB(PInt left, boolean right,
222+
@Shared("ctxt") @CachedContext(PythonLanguage.class) PythonContext ctxt) {
223+
return doBP(right, left, ctxt);
224+
}
225+
226+
@Specialization
227+
boolean doPI(PInt left, int right,
228+
@Shared("isBuiltin") @Cached IsBuiltinClassProfile isBuiltin) {
229+
return doIP(right, left, isBuiltin);
230+
}
231+
232+
@Specialization
233+
boolean doPL(PInt left, long right,
234+
@Shared("isBuiltin") @Cached IsBuiltinClassProfile isBuiltin) {
235+
return doLP(right, left, isBuiltin);
236+
}
237+
238+
// types
194239
@Specialization
195240
boolean doCT(PythonBuiltinClass left, PythonBuiltinClassType right) {
196241
return left.getType() == right;
@@ -201,33 +246,40 @@ boolean doTC(PythonBuiltinClassType left, PythonBuiltinClass right) {
201246
return right.getType() == left;
202247
}
203248

249+
// native objects
204250
@Specialization
205251
boolean doNative(PythonAbstractNativeObject left, PythonAbstractNativeObject right,
206252
@Cached CExtNodes.PointerCompareNode isNode) {
207253
return isNode.execute(__EQ__, left, right);
208254
}
209255

256+
// code
210257
@Specialization
211258
boolean doCode(PCode left, PCode right) {
212259
// Special case for code objects: Frames create them on-demand even if they refer to the
213260
// 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();
261+
if (left != right) {
262+
RootCallTarget leftCt = left.getRootCallTarget();
263+
RootCallTarget rightCt = right.getRootCallTarget();
264+
if (leftCt != null && rightCt != null) {
265+
// TODO: handle splitting, i.e., cloned root nodes
266+
return leftCt.getRootNode() == rightCt.getRootNode();
267+
} else {
268+
return false;
269+
}
219270
}
220-
return left == right;
271+
return true;
221272
}
222273

274+
// none
223275
@Specialization
224276
boolean doObjectPNone(Object left, PNone right,
225277
@Cached.Shared("ctxt") @CachedContext(PythonLanguage.class) PythonContext ctxt) {
226278
if (ctxt.getOption(PythonOptions.EmulateJython) && ctxt.getEnv().isHostObject(left) && ctxt.getEnv().asHostObject(left) == null &&
227279
right == PNone.NONE) {
228280
return true;
229281
}
230-
return doGeneric(left, right);
282+
return left == right;
231283
}
232284

233285
@Specialization
@@ -236,9 +288,22 @@ boolean doPNoneObject(PNone left, Object right,
236288
return doObjectPNone(right, left, ctxt);
237289
}
238290

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

244309
public static IsNode create() {

0 commit comments

Comments
 (0)