Skip to content

Commit ee8ddc5

Browse files
committed
implement isTrue for Python objects
1 parent 729d28a commit ee8ddc5

File tree

4 files changed

+178
-5
lines changed

4 files changed

+178
-5
lines changed

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
package com.oracle.graal.python.builtins.objects;
2727

2828
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
29+
import com.oracle.graal.python.builtins.objects.function.PArguments.ThreadState;
2930
import com.oracle.graal.python.builtins.objects.object.PythonObjectLibrary;
3031
import com.oracle.graal.python.builtins.objects.type.LazyPythonClass;
3132
import com.oracle.truffle.api.CompilerAsserts;
@@ -65,4 +66,10 @@ static boolean isNull(@SuppressWarnings("unused") PNone self) {
6566
public LazyPythonClass getLazyPythonClass() {
6667
return PythonBuiltinClassType.PNone;
6768
}
69+
70+
@ExportMessage
71+
@SuppressWarnings("static-method")
72+
boolean isTrueWithState(@SuppressWarnings("unused") ThreadState state) {
73+
return false;
74+
}
6875
}

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

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

4343
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.AttributeError;
44+
import static com.oracle.graal.python.nodes.SpecialMethodNames.__BOOL__;
4445
import static com.oracle.graal.python.nodes.SpecialMethodNames.__CALL__;
4546
import static com.oracle.graal.python.nodes.SpecialMethodNames.__DELETE__;
4647
import static com.oracle.graal.python.nodes.SpecialMethodNames.__DELITEM__;
@@ -53,6 +54,7 @@
5354
import static com.oracle.graal.python.nodes.SpecialMethodNames.__HASH__;
5455
import static com.oracle.graal.python.nodes.SpecialMethodNames.__INDEX__;
5556
import static com.oracle.graal.python.nodes.SpecialMethodNames.__ITER__;
57+
import static com.oracle.graal.python.nodes.SpecialMethodNames.__LEN__;
5658
import static com.oracle.graal.python.nodes.SpecialMethodNames.__NEXT__;
5759
import static com.oracle.graal.python.nodes.SpecialMethodNames.__SETITEM__;
5860
import static com.oracle.graal.python.nodes.SpecialMethodNames.__SET__;
@@ -107,6 +109,7 @@
107109
import com.oracle.graal.python.nodes.object.GetClassNode;
108110
import com.oracle.graal.python.nodes.object.GetLazyClassNode;
109111
import com.oracle.graal.python.nodes.object.IsBuiltinClassProfile;
112+
import com.oracle.graal.python.nodes.util.CastToJavaBooleanNode;
110113
import com.oracle.graal.python.nodes.util.CastToJavaIntNode;
111114
import com.oracle.graal.python.nodes.util.CastToJavaLongNode;
112115
import com.oracle.graal.python.runtime.exception.PException;
@@ -377,7 +380,7 @@ private boolean isInBounds(LookupAndCallUnaryDynamicNode callLenNode, PInteropSu
377380
}
378381

379382
private long getArraySizeSafe(LookupAndCallUnaryDynamicNode callLenNode) {
380-
Object lenObj = callLenNode.executeObject(this, SpecialMethodNames.__LEN__);
383+
Object lenObj = callLenNode.executeObject(this, __LEN__);
381384
if (lenObj instanceof Number) {
382385
return ((Number) lenObj).longValue();
383386
} else if (lenObj instanceof PInt) {
@@ -599,7 +602,7 @@ public int lengthWithState(ThreadState state,
599602
@Exclusive @Cached CallNode callNode,
600603
@Exclusive @Cached PRaiseNode raiseNode,
601604
@Exclusive @CachedLibrary(limit = "1") PythonObjectLibrary lib) {
602-
Object lenFunc = getLenNode.execute(this, SpecialMethodNames.__LEN__);
605+
Object lenFunc = getLenNode.execute(this, __LEN__);
603606
if (hasLen.profile(lenFunc != PNone.NO_VALUE)) {
604607
Object lenResult;
605608
int len;
@@ -635,8 +638,8 @@ public boolean isSequenceType(
635638
@Shared("getItemProfile") @Cached("createBinaryProfile()") ConditionProfile getItemProfile) {
636639
if (isLazyClass.profile(this instanceof LazyPythonClass)) {
637640
LazyPythonClass type = (LazyPythonClass) this; // guaranteed to succeed because of guard
638-
if (lenProfile.profile(hasLenNode.execute(type, SpecialMethodNames.__LEN__) != PNone.NO_VALUE)) {
639-
return getItemProfile.profile(hasGetItemNode.execute(type, SpecialMethodNames.__GETITEM__) != PNone.NO_VALUE);
641+
if (lenProfile.profile(hasLenNode.execute(type, __LEN__) != PNone.NO_VALUE)) {
642+
return getItemProfile.profile(hasGetItemNode.execute(type, __GETITEM__) != PNone.NO_VALUE);
640643
}
641644
}
642645
return false;
@@ -694,6 +697,50 @@ public final boolean isCallable(@Exclusive @Cached LookupInheritedAttributeNode.
694697
return PGuards.isCallable(call);
695698
}
696699

700+
@ExportMessage
701+
public boolean isTrueWithState(ThreadState state,
702+
@Exclusive @Cached("createBinaryProfile()") ConditionProfile gotState,
703+
@Exclusive @Cached("createBinaryProfile()") ConditionProfile hasBool,
704+
@Exclusive @Cached("createBinaryProfile()") ConditionProfile hasLen,
705+
@Exclusive @Cached LookupInheritedAttributeNode.Dynamic lookupAttrs,
706+
@Exclusive @Cached CastToJavaBooleanNode castToBoolean,
707+
@Exclusive @Cached PRaiseNode raiseNode,
708+
@Exclusive @CachedLibrary("this") PythonObjectLibrary lib,
709+
@Exclusive @Cached CallNode callNode) {
710+
// n.b.: CPython's early returns for PyTrue/PyFalse/PyNone are handled
711+
// in the message impls in PNone and PInt
712+
Object boolAttr = lookupAttrs.execute(this, __BOOL__);
713+
if (hasBool.profile(boolAttr != PNone.NO_VALUE)) {
714+
// this inlines the work done in sq_nb_bool when __bool__ is used.
715+
// when __len__ would be used, this is the same as the branch below
716+
// calling __len__
717+
Object result;
718+
if (gotState.profile(state == null)) {
719+
result = callNode.execute(boolAttr, this);
720+
} else {
721+
result = callNode.execute(PArguments.frameForCall(state), boolAttr, this);
722+
}
723+
try {
724+
return castToBoolean.execute(result);
725+
} catch (CastToJavaBooleanNode.CannotCastException e) {
726+
// cast node will act as a branch profile already for the compiler
727+
throw raiseNode.raise(PythonBuiltinClassType.TypeError, "__bool__ should return bool, returned %p", result);
728+
}
729+
} else {
730+
Object lenAttr = lookupAttrs.execute(this, __LEN__);
731+
if (hasLen.profile(lenAttr != PNone.NO_VALUE)) {
732+
if (gotState.profile(state == null)) {
733+
return lib.length(this) > 0;
734+
} else {
735+
return lib.lengthWithState(this, state) > 0;
736+
}
737+
} else {
738+
// like CPython, anything else is true-ish
739+
return true;
740+
}
741+
}
742+
}
743+
697744
@ExportMessage
698745
public final boolean isHashable(@Exclusive @Cached LookupInheritedAttributeNode.Dynamic lookupHashAttributeNode,
699746
@CachedLibrary(limit = "1") PythonObjectLibrary dataModelLibrary) {
@@ -1288,7 +1335,7 @@ Object doSpecialObject(Object primary, Object index,
12881335
@Cached CallNode callGetItemNode,
12891336
@Cached PRaiseNode raiseNode,
12901337
@Cached("createBinaryProfile()") ConditionProfile profile) {
1291-
Object attrGetItem = lookupGetItemNode.execute(primary, SpecialMethodNames.__GETITEM__);
1338+
Object attrGetItem = lookupGetItemNode.execute(primary, __GETITEM__);
12921339
if (profile.profile(attrGetItem == PNone.NO_VALUE)) {
12931340
throw raiseNode.raise(PythonBuiltinClassType.TypeError, "'%p' object is not subscriptable", primary);
12941341
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ints/PInt.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,11 @@ public Object asIndexWithState(@SuppressWarnings("unused") ThreadState threadSta
198198
return this;
199199
}
200200

201+
@ExportMessage
202+
boolean isTrueWithState(@SuppressWarnings("unused") ThreadState threadState) {
203+
return !isZero();
204+
}
205+
201206
@Override
202207
public int hashCode() {
203208
return value.hashCode();
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.graal.python.nodes.util;
42+
43+
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
44+
import com.oracle.graal.python.builtins.objects.cext.PythonNativeObject;
45+
import com.oracle.graal.python.builtins.objects.ints.PInt;
46+
import com.oracle.graal.python.nodes.PGuards;
47+
import com.oracle.graal.python.nodes.PNodeWithContext;
48+
import com.oracle.graal.python.nodes.classes.IsSubtypeNode;
49+
import com.oracle.graal.python.nodes.object.GetLazyClassNode;
50+
import com.oracle.truffle.api.CompilerDirectives;
51+
import com.oracle.truffle.api.dsl.Cached;
52+
import com.oracle.truffle.api.dsl.Fallback;
53+
import com.oracle.truffle.api.dsl.GenerateUncached;
54+
import com.oracle.truffle.api.dsl.ImportStatic;
55+
import com.oracle.truffle.api.dsl.Specialization;
56+
import com.oracle.truffle.api.nodes.ControlFlowException;
57+
import com.oracle.truffle.api.profiles.ConditionProfile;
58+
59+
/**
60+
* Casts a Python boolean to a Java boolean without coercion. <b>ATTENTION:</b> If the cast fails,
61+
* because the object is not a Python boolean, the node will throw a {@link CannotCastException}.
62+
*/
63+
@GenerateUncached
64+
@ImportStatic(PGuards.class)
65+
public abstract class CastToJavaBooleanNode extends PNodeWithContext {
66+
67+
public static final class CannotCastException extends ControlFlowException {
68+
private static final long serialVersionUID = 1L;
69+
private static final CannotCastException INSTANCE = new CannotCastException();
70+
}
71+
72+
public abstract boolean execute(Object x) throws CannotCastException;
73+
74+
@Specialization
75+
static boolean doBoolean(boolean x) {
76+
return x;
77+
}
78+
79+
@Specialization
80+
static boolean doPInt(PInt x,
81+
@Cached("createBinaryProfile()") ConditionProfile isBoolean,
82+
@Cached IsSubtypeNode isSubtype) {
83+
if (isBoolean.profile(isSubtype.execute(x.getLazyPythonClass(), PythonBuiltinClassType.Boolean))) {
84+
return !x.isZero();
85+
} else {
86+
throw CannotCastException.INSTANCE;
87+
}
88+
}
89+
90+
@Specialization
91+
static boolean doNativeObject(PythonNativeObject x,
92+
@Cached GetLazyClassNode getClassNode,
93+
@Cached IsSubtypeNode isSubtypeNode) {
94+
if (isSubtypeNode.execute(getClassNode.execute(x), PythonBuiltinClassType.Boolean)) {
95+
CompilerDirectives.transferToInterpreter();
96+
throw new RuntimeException("casting a native long object to a Java boolean is not implemented yet");
97+
}
98+
// the object's type is not a subclass of 'int'
99+
throw CannotCastException.INSTANCE;
100+
}
101+
102+
@Fallback
103+
static boolean doUnsupported(@SuppressWarnings("unused") Object x) {
104+
throw CannotCastException.INSTANCE;
105+
}
106+
107+
public static CastToJavaBooleanNode create() {
108+
return CastToJavaBooleanNodeGen.create();
109+
}
110+
111+
public static CastToJavaBooleanNode getUncached() {
112+
return CastToJavaBooleanNodeGen.getUncached();
113+
}
114+
}

0 commit comments

Comments
 (0)