Skip to content

Commit 65d11bf

Browse files
committed
Handle the case when special methods are descriptors but not functions
1 parent 26064fd commit 65d11bf

File tree

11 files changed

+211
-33
lines changed

11 files changed

+211
-33
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
2+
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
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
8+
# data (collectively the "Software"), free of charge and under any and all
9+
# copyright rights in the Software, and any and all patent rights owned or
10+
# freely licensable by each licensor hereunder covering either (i) the
11+
# unmodified Software as contributed to or provided by such licensor, or (ii)
12+
# the Larger Works (as defined below), to deal in both
13+
#
14+
# (a) the Software, and
15+
#
16+
# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
17+
# one is included with the Software each a "Larger Work" to which the Software
18+
# is contributed by such licensors),
19+
#
20+
# without restriction, including without limitation the rights to copy, create
21+
# derivative works of, display, perform, and distribute the Software and make,
22+
# use, sell, offer for sale, import, export, have made, and have sold the
23+
# Software and the Larger Work(s), and to sublicense the foregoing rights on
24+
# either these or other terms.
25+
#
26+
# This license is subject to the following condition:
27+
#
28+
# The above copyright notice and either this complete permission notice or at a
29+
# minimum a reference to the UPL must be included in all copies or substantial
30+
# portions of the Software.
31+
#
32+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38+
# SOFTWARE.
39+
40+
def test_special_descriptor():
41+
class A:
42+
@property
43+
def __eq__(self):
44+
def inner(other):
45+
return True
46+
return inner
47+
48+
assert A() == A()

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/CallBinaryMethodNode.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import com.oracle.graal.python.builtins.objects.function.PKeyword;
4545
import com.oracle.graal.python.builtins.objects.method.PBuiltinMethod;
4646
import com.oracle.graal.python.nodes.call.CallNode;
47+
import com.oracle.graal.python.nodes.call.special.LookupSpecialMethodNode.BoundDescriptor;
4748
import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
4849
import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode;
4950
import com.oracle.truffle.api.RootCallTarget;
@@ -54,6 +55,7 @@
5455
import com.oracle.truffle.api.frame.Frame;
5556
import com.oracle.truffle.api.frame.VirtualFrame;
5657
import com.oracle.truffle.api.nodes.UnexpectedResultException;
58+
import com.oracle.truffle.api.profiles.ConditionProfile;
5759

5860
@GenerateUncached
5961
@ReportPolymorphism
@@ -470,7 +472,12 @@ Object callSelfMethod(VirtualFrame frame, @SuppressWarnings("unused") PBuiltinMe
470472
"callBoolLongReverse", "callDoubleSingleReverse", "callDoubleReverse", "callBoolDoubleSingleReverse", "callBoolDoubleReverse", "callObjectSingleContextReverse",
471473
"callObjectReverse", "callMethodSingleContext", "callSelfMethodSingleContext", "callMethod", "callSelfMethod"})
472474
static Object call(VirtualFrame frame, Object func, Object arg1, Object arg2,
473-
@Cached CallNode callNode) {
474-
return callNode.execute(frame, func, new Object[]{arg1, arg2}, PKeyword.EMPTY_KEYWORDS);
475+
@Cached CallNode callNode,
476+
@Cached ConditionProfile isBoundProfile) {
477+
if (isBoundProfile.profile(func instanceof BoundDescriptor)) {
478+
return callNode.execute(frame, ((BoundDescriptor) func).descriptor, new Object[]{arg2}, PKeyword.EMPTY_KEYWORDS);
479+
} else {
480+
return callNode.execute(frame, func, new Object[]{arg1, arg2}, PKeyword.EMPTY_KEYWORDS);
481+
}
475482
}
476483
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/CallQuaternaryMethodNode.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,13 @@
4444
import com.oracle.graal.python.builtins.objects.function.PKeyword;
4545
import com.oracle.graal.python.builtins.objects.method.PBuiltinMethod;
4646
import com.oracle.graal.python.nodes.call.CallNode;
47+
import com.oracle.graal.python.nodes.call.special.LookupSpecialMethodNode.BoundDescriptor;
4748
import com.oracle.graal.python.nodes.function.builtins.PythonQuaternaryBuiltinNode;
4849
import com.oracle.truffle.api.RootCallTarget;
4950
import com.oracle.truffle.api.dsl.Cached;
5051
import com.oracle.truffle.api.dsl.Specialization;
5152
import com.oracle.truffle.api.frame.VirtualFrame;
53+
import com.oracle.truffle.api.profiles.ConditionProfile;
5254

5355
public abstract class CallQuaternaryMethodNode extends CallSpecialMethodNode {
5456
public static CallQuaternaryMethodNode create() {
@@ -94,7 +96,12 @@ Object callMethod(VirtualFrame frame, @SuppressWarnings("unused") PBuiltinMethod
9496

9597
@Specialization(replaces = {"callSingle", "call", "callMethodSingle", "callMethod"})
9698
Object generic(VirtualFrame frame, Object func, Object arg1, Object arg2, Object arg3, Object arg4,
97-
@Cached CallNode callNode) {
98-
return callNode.execute(frame, func, new Object[]{arg1, arg2, arg3, arg4}, PKeyword.EMPTY_KEYWORDS);
99+
@Cached CallNode callNode,
100+
@Cached ConditionProfile isBoundProfile) {
101+
if (isBoundProfile.profile(func instanceof BoundDescriptor)) {
102+
return callNode.execute(frame, ((BoundDescriptor) func).descriptor, new Object[]{arg2, arg3, arg4}, PKeyword.EMPTY_KEYWORDS);
103+
} else {
104+
return callNode.execute(frame, func, new Object[]{arg1, arg2, arg3, arg4}, PKeyword.EMPTY_KEYWORDS);
105+
}
99106
}
100107
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/CallTernaryMethodNode.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import com.oracle.graal.python.builtins.objects.function.PKeyword;
4545
import com.oracle.graal.python.builtins.objects.method.PBuiltinMethod;
4646
import com.oracle.graal.python.nodes.call.CallNode;
47+
import com.oracle.graal.python.nodes.call.special.LookupSpecialMethodNode.BoundDescriptor;
4748
import com.oracle.graal.python.nodes.function.builtins.PythonQuaternaryBuiltinNode;
4849
import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode;
4950
import com.oracle.truffle.api.RootCallTarget;
@@ -53,6 +54,7 @@
5354
import com.oracle.truffle.api.dsl.Specialization;
5455
import com.oracle.truffle.api.frame.Frame;
5556
import com.oracle.truffle.api.frame.VirtualFrame;
57+
import com.oracle.truffle.api.profiles.ConditionProfile;
5658

5759
@ReportPolymorphism
5860
@GenerateUncached
@@ -207,7 +209,12 @@ Object callSelfMethod(VirtualFrame frame, @SuppressWarnings("unused") PBuiltinMe
207209
"doBuiltinMethodOIOCached", "doBuiltinMethodCached", "doBuiltinMethodOIOCtCached", "doBuiltinMethodCtCached", "callSelfMethodSingleContext",
208210
"callSelfMethod"})
209211
static Object call(VirtualFrame frame, Object func, Object arg1, Object arg2, Object arg3,
210-
@Cached CallNode callNode) {
211-
return callNode.execute(frame, func, new Object[]{arg1, arg2, arg3}, PKeyword.EMPTY_KEYWORDS);
212+
@Cached CallNode callNode,
213+
@Cached ConditionProfile isBoundProfile) {
214+
if (isBoundProfile.profile(func instanceof BoundDescriptor)) {
215+
return callNode.execute(frame, ((BoundDescriptor) func).descriptor, new Object[]{arg2, arg3}, PKeyword.EMPTY_KEYWORDS);
216+
} else {
217+
return callNode.execute(frame, func, new Object[]{arg1, arg2, arg3}, PKeyword.EMPTY_KEYWORDS);
218+
}
212219
}
213220
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/CallUnaryMethodNode.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import com.oracle.graal.python.builtins.objects.function.PKeyword;
4545
import com.oracle.graal.python.builtins.objects.method.PBuiltinMethod;
4646
import com.oracle.graal.python.nodes.call.CallNode;
47+
import com.oracle.graal.python.nodes.call.special.LookupSpecialMethodNode.BoundDescriptor;
4748
import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
4849
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
4950
import com.oracle.truffle.api.RootCallTarget;
@@ -54,6 +55,7 @@
5455
import com.oracle.truffle.api.frame.Frame;
5556
import com.oracle.truffle.api.frame.VirtualFrame;
5657
import com.oracle.truffle.api.nodes.UnexpectedResultException;
58+
import com.oracle.truffle.api.profiles.ConditionProfile;
5759

5860
@ReportPolymorphism
5961
@GenerateUncached
@@ -220,7 +222,12 @@ Object callSelfMethod(VirtualFrame frame, @SuppressWarnings("unused") PBuiltinMe
220222
@Specialization(replaces = {"callIntSingle", "callInt", "callLongSingle", "callLong", "callDoubleSingle", "callDouble", "callBoolSingle", "callBool", "callObjectSingleContext",
221223
"callMethodSingleContext", "callSelfMethodSingleContext", "callMethod", "callSelfMethod"})
222224
static Object call(VirtualFrame frame, Object func, Object receiver,
223-
@Cached("create()") CallNode callNode) {
224-
return callNode.execute(frame, func, new Object[]{receiver}, PKeyword.EMPTY_KEYWORDS);
225+
@Cached("create()") CallNode callNode,
226+
@Cached ConditionProfile isBoundProfile) {
227+
if (isBoundProfile.profile(func instanceof BoundDescriptor)) {
228+
return callNode.execute(frame, ((BoundDescriptor) func).descriptor, new Object[0], PKeyword.EMPTY_KEYWORDS);
229+
} else {
230+
return callNode.execute(frame, func, new Object[]{receiver}, PKeyword.EMPTY_KEYWORDS);
231+
}
225232
}
226233
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/CallVarargsMethodNode.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,18 @@
4545
import com.oracle.graal.python.builtins.objects.function.PKeyword;
4646
import com.oracle.graal.python.builtins.objects.method.PBuiltinMethod;
4747
import com.oracle.graal.python.nodes.call.CallNode;
48+
import com.oracle.graal.python.nodes.call.special.LookupSpecialMethodNode.BoundDescriptor;
4849
import com.oracle.graal.python.nodes.function.builtins.PythonVarargsBuiltinNode;
4950
import com.oracle.graal.python.nodes.function.builtins.PythonVarargsBuiltinNode.VarargsBuiltinDirectInvocationNotSupported;
51+
import com.oracle.graal.python.util.PythonUtils;
5052
import com.oracle.truffle.api.RootCallTarget;
5153
import com.oracle.truffle.api.dsl.Cached;
5254
import com.oracle.truffle.api.dsl.GenerateUncached;
5355
import com.oracle.truffle.api.dsl.ReportPolymorphism;
5456
import com.oracle.truffle.api.dsl.Specialization;
5557
import com.oracle.truffle.api.frame.Frame;
5658
import com.oracle.truffle.api.frame.VirtualFrame;
59+
import com.oracle.truffle.api.profiles.ConditionProfile;
5760

5861
@GenerateUncached
5962
@ReportPolymorphism
@@ -134,7 +137,14 @@ Object callQuaternary(VirtualFrame frame, Object callable, Object[] arguments, @
134137

135138
@Specialization(replaces = {"callVarargsDirect", "callVarargs", "callSelfMethodSingleContext", "callSelfMethod", "callUnary", "callBinary", "callTernary", "callQuaternary"})
136139
Object call(VirtualFrame frame, Object func, Object[] arguments, PKeyword[] keywords,
137-
@Cached CallNode callNode) {
138-
return callNode.execute(frame, func, arguments, keywords);
140+
@Cached CallNode callNode,
141+
@Cached ConditionProfile isBoundProfile) {
142+
if (isBoundProfile.profile(func instanceof BoundDescriptor)) {
143+
Object[] boundArguments = new Object[arguments.length - 1];
144+
PythonUtils.arraycopy(arguments, 1, boundArguments, 0, boundArguments.length);
145+
return callNode.execute(frame, ((BoundDescriptor) func).descriptor, boundArguments, keywords);
146+
} else {
147+
return callNode.execute(frame, func, arguments, keywords);
148+
}
139149
}
140150
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/LookupAndCallBinaryNode.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ public static LookupAndCallBinaryNode create(String name, String rname, Supplier
132132
}
133133

134134
protected Object getMethod(Object receiver, String methodName) {
135-
return LookupSpecialMethodNode.Dynamic.getUncached().execute(GetClassNode.getUncached().execute(receiver), methodName);
135+
return LookupSpecialMethodNode.Dynamic.getUncached().execute(GetClassNode.getUncached().execute(receiver), methodName, receiver, true);
136136
}
137137

138138
protected boolean isReversible() {
@@ -286,7 +286,7 @@ Object callObject(VirtualFrame frame, Object left, Object right,
286286
@SuppressWarnings("unused") @CachedLibrary("left") PythonObjectLibrary libLeft,
287287
@SuppressWarnings("unused") @CachedLibrary("right") PythonObjectLibrary libRight,
288288
@Cached("create(name)") LookupSpecialMethodNode getattr) {
289-
Object leftCallable = getattr.execute(libLeft.getLazyPythonClass(left));
289+
Object leftCallable = getattr.execute(frame, libLeft.getLazyPythonClass(left), left);
290290
if (leftCallable == PNone.NO_VALUE) {
291291
if (handlerFactory != null) {
292292
return runErrorHandler(left, right);
@@ -318,9 +318,9 @@ Object callObject(VirtualFrame frame, Object left, Object right,
318318

319319
Object result = PNotImplemented.NOT_IMPLEMENTED;
320320
Object leftClass = libLeft.getLazyPythonClass(left);
321-
Object leftCallable = getattr.execute(leftClass);
321+
Object leftCallable = getattr.execute(frame, leftClass, left);
322322
Object rightClass = libRight.getLazyPythonClass(right);
323-
Object rightCallable = getattrR.execute(rightClass);
323+
Object rightCallable = getattrR.execute(frame, rightClass, right);
324324
if (!alwaysCheckReverse && leftCallable == rightCallable) {
325325
rightCallable = PNone.NO_VALUE;
326326
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/LookupAndCallTernaryNode.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public abstract static class NotImplementedHandler extends PNodeWithContext {
7070
@Child private CallTernaryMethodNode reverseDispatchNode;
7171
@Child private CallTernaryMethodNode thirdDispatchNode;
7272
@Child private LookupSpecialMethodNode getThirdAttrNode;
73+
@Child private GetClassNode thirdGetClassNode;
7374
@Child private NotImplementedHandler handler;
7475
protected final Supplier<NotImplementedHandler> handlerFactory;
7576

@@ -146,6 +147,14 @@ private CallTernaryMethodNode ensureThirdDispatch() {
146147
return thirdDispatchNode;
147148
}
148149

150+
private GetClassNode ensureThirdGetClass() {
151+
if (thirdGetClassNode == null) {
152+
CompilerDirectives.transferToInterpreterAndInvalidate();
153+
thirdGetClassNode = insert(GetClassNode.create());
154+
}
155+
return thirdGetClassNode;
156+
}
157+
149158
@Specialization(guards = "isReversible()")
150159
Object callObject(
151160
VirtualFrame frame,
@@ -169,11 +178,11 @@ Object callObject(
169178
Object rightClass = getClassR.execute(w);
170179

171180
Object result = PNotImplemented.NOT_IMPLEMENTED;
172-
Object leftCallable = getattr.execute(leftClass);
181+
Object leftCallable = getattr.execute(frame, leftClass, v);
173182
Object rightCallable = PNone.NO_VALUE;
174183

175184
if (!isSameTypeNode.execute(leftClass, rightClass)) {
176-
rightCallable = getattrR.execute(rightClass);
185+
rightCallable = getattrR.execute(frame, rightClass, w);
177186
if (rightCallable == leftCallable) {
178187
rightCallable = PNone.NO_VALUE;
179188
}
@@ -198,9 +207,9 @@ Object callObject(
198207
}
199208
}
200209

201-
Object zCallable = ensureGetAttrZ().execute(z);
210+
Object zCallable = ensureGetAttrZ().execute(frame, ensureThirdGetClass().execute(z), z);
202211
if (zCallable != PNone.NO_VALUE && zCallable != leftCallable && zCallable != rightCallable) {
203-
ensureThirdDispatch().execute(frame, zCallable, v, w, z);
212+
result = ensureThirdDispatch().execute(frame, zCallable, v, w, z);
204213
if (result != PNotImplemented.NOT_IMPLEMENTED) {
205214
return result;
206215
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/LookupAndCallUnaryNode.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ Object callObject(VirtualFrame frame, Object receiver,
210210
@CachedLibrary("receiver") PythonObjectLibrary lib,
211211
@Cached("create(name)") LookupSpecialMethodNode getattr,
212212
@Cached("create()") CallUnaryMethodNode dispatchNode) {
213-
Object attr = getattr.execute(lib.getLazyPythonClass(receiver));
213+
Object attr = getattr.execute(frame, lib.getLazyPythonClass(receiver), receiver);
214214
if (attr == PNone.NO_VALUE) {
215215
if (handlerFactory != null) {
216216
if (handler == null) {
@@ -236,8 +236,7 @@ static Object doObject(Object receiver, String name,
236236
@Cached LookupSpecialMethodNode.Dynamic getattr,
237237
@Cached CallUnaryMethodNode dispatchNode,
238238
@Cached("createBinaryProfile()") ConditionProfile profile) {
239-
240-
Object attr = getattr.execute(lib.getLazyPythonClass(receiver), name);
239+
Object attr = getattr.execute(lib.getLazyPythonClass(receiver), name, receiver, true);
241240
if (profile.profile(attr != PNone.NO_VALUE)) {
242241
// NOTE: it's safe to pass a 'null' frame since this node can only be used via a
243242
// global state context manager

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/LookupAndCallVarargsNode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,6 @@ public static LookupAndCallVarargsNode create(String name) {
6666
Object callObject(VirtualFrame frame, Object callable, Object[] arguments,
6767
@CachedLibrary("callable") PythonObjectLibrary plib,
6868
@Cached("create(name)") LookupSpecialMethodNode getattr) {
69-
return dispatchNode.execute(frame, getattr.execute(plib.getLazyPythonClass(callable)), arguments, PKeyword.EMPTY_KEYWORDS);
69+
return dispatchNode.execute(frame, getattr.execute(frame, plib.getLazyPythonClass(callable), callable), arguments, PKeyword.EMPTY_KEYWORDS);
7070
}
7171
}

0 commit comments

Comments
 (0)