Skip to content

Commit 22509be

Browse files
committed
[GR-12482] Intrinsify classmethod and staticmethod
PullRequest: graalpython/269
2 parents 64699f1 + 81bce74 commit 22509be

File tree

19 files changed

+450
-193
lines changed

19 files changed

+450
-193
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@
5959

6060
boolean isPublic() default true;
6161

62+
boolean isClassmethod() default false;
63+
64+
boolean isStaticmethod() default false;
65+
6266
/**
6367
* By default the caller frame bit is set on-demand, but for some builtins it might be useful to
6468
* always force passing the caller frame.

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,10 @@
125125
import com.oracle.graal.python.builtins.objects.memoryview.MemoryviewBuiltins;
126126
import com.oracle.graal.python.builtins.objects.method.AbstractMethodBuiltins;
127127
import com.oracle.graal.python.builtins.objects.method.BuiltinMethodBuiltins;
128+
import com.oracle.graal.python.builtins.objects.method.ClassmethodBuiltins;
129+
import com.oracle.graal.python.builtins.objects.method.DecoratedMethodBuiltins;
128130
import com.oracle.graal.python.builtins.objects.method.MethodBuiltins;
131+
import com.oracle.graal.python.builtins.objects.method.StaticmethodBuiltins;
129132
import com.oracle.graal.python.builtins.objects.module.PythonModule;
130133
import com.oracle.graal.python.builtins.objects.object.ObjectBuiltins;
131134
import com.oracle.graal.python.builtins.objects.object.PythonObject;
@@ -190,7 +193,6 @@ private static final String[] initializeCoreFiles() {
190193
"_warnings",
191194
"posix",
192195
"_io",
193-
"int",
194196
"_frozen_importlib",
195197
"classes",
196198
"_weakref",
@@ -204,7 +206,6 @@ private static final String[] initializeCoreFiles() {
204206
"_codecs",
205207
"bytes",
206208
"bytearray",
207-
"float",
208209
"time",
209210
"unicodedata",
210211
"_locale",
@@ -224,6 +225,9 @@ private static final PythonBuiltins[] initializeBuiltins() {
224225
List<PythonBuiltins> builtins = new ArrayList<>(Arrays.asList(
225226
new BuiltinConstructors(),
226227
new BuiltinFunctions(),
228+
new DecoratedMethodBuiltins(),
229+
new ClassmethodBuiltins(),
230+
new StaticmethodBuiltins(),
227231
new InteropModuleBuiltins(),
228232
new ObjectBuiltins(),
229233
new CellBuiltins(),

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ public enum PythonBuiltinClassType implements LazyPythonClass {
9191
PLock("LockType", "_thread"),
9292
PRLock("RLock", "_thread"),
9393
PSocket("socket", "_socket"),
94+
PStaticmethod("staticmethod", "builtins"),
95+
PClassmethod("classmethod", "builtins"),
9496

9597
// Errors and exceptions:
9698

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ public void initialize(PythonCore core) {
7373
RootCallTarget callTarget = core.getLanguage().builtinCallTargetCache.computeIfAbsent(factory.getNodeClass(),
7474
(b) -> Truffle.getRuntime().createCallTarget(new BuiltinFunctionRootNode(core.getLanguage(), builtin, factory, declaresExplicitSelf)));
7575
if (builtin.constructsClass().length > 0) {
76+
assert !builtin.isGetter() && !builtin.isSetter() && !builtin.isClassmethod() && !builtin.isStaticmethod();
7677
PBuiltinFunction newFunc = core.factory().createBuiltinFunction(__NEW__, null, createArity(builtin, declaresExplicitSelf), callTarget);
7778
for (PythonBuiltinClassType type : builtin.constructsClass()) {
7879
PythonBuiltinClass builtinClass = core.lookupType(type);
@@ -84,9 +85,15 @@ public void initialize(PythonCore core) {
8485
function.setAttribute(__DOC__, builtin.doc());
8586
BoundBuiltinCallable<?> callable = function;
8687
if (builtin.isGetter() || builtin.isSetter()) {
88+
assert !builtin.isClassmethod() && !builtin.isStaticmethod();
8789
PythonCallable get = builtin.isGetter() ? function : null;
8890
PythonCallable set = builtin.isSetter() ? function : null;
8991
callable = core.factory().createGetSetDescriptor(get, set, builtin.name(), null);
92+
} else if (builtin.isClassmethod()) {
93+
assert !builtin.isStaticmethod();
94+
callable = core.factory().createClassmethod(function);
95+
} else if (builtin.isStaticmethod()) {
96+
callable = core.factory().createStaticmethod(function);
9097
}
9198
setBuiltinFunction(builtin.name(), callable);
9299
}

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

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import static com.oracle.graal.python.nodes.BuiltinNames.BOOL;
3030
import static com.oracle.graal.python.nodes.BuiltinNames.BYTEARRAY;
3131
import static com.oracle.graal.python.nodes.BuiltinNames.BYTES;
32+
import static com.oracle.graal.python.nodes.BuiltinNames.CLASSMETHOD;
3233
import static com.oracle.graal.python.nodes.BuiltinNames.COMPLEX;
3334
import static com.oracle.graal.python.nodes.BuiltinNames.DICT;
3435
import static com.oracle.graal.python.nodes.BuiltinNames.ENUMERATE;
@@ -42,6 +43,7 @@
4243
import static com.oracle.graal.python.nodes.BuiltinNames.RANGE;
4344
import static com.oracle.graal.python.nodes.BuiltinNames.REVERSED;
4445
import static com.oracle.graal.python.nodes.BuiltinNames.SET;
46+
import static com.oracle.graal.python.nodes.BuiltinNames.STATICMETHOD;
4547
import static com.oracle.graal.python.nodes.BuiltinNames.STR;
4648
import static com.oracle.graal.python.nodes.BuiltinNames.SUPER;
4749
import static com.oracle.graal.python.nodes.BuiltinNames.TUPLE;
@@ -2137,4 +2139,41 @@ Object doObjectIndirect(PythonClass self, @SuppressWarnings("unused") Object typ
21372139
return factory().createSuperObject(self);
21382140
}
21392141
}
2142+
2143+
@Builtin(name = CLASSMETHOD, fixedNumOfPositionalArgs = 2, constructsClass = PythonBuiltinClassType.PClassmethod, doc = "classmethod(function) -> method\n" +
2144+
"\n" +
2145+
"Convert a function to be a class method.\n" +
2146+
"\n" +
2147+
"A class method receives the class as implicit first argument,\n" +
2148+
"just like an instance method receives the instance.\n" +
2149+
"To declare a class method, use this idiom:\n" +
2150+
"\n" +
2151+
" class C:\n" +
2152+
" @classmethod\n" +
2153+
" def f(cls, arg1, arg2, ...):\n" +
2154+
" ...\n" +
2155+
"\n" +
2156+
"It can be called either on the class (e.g. C.f()) or on an instance\n" +
2157+
"(e.g. C().f()). The instance is ignored except for its class.\n" +
2158+
"If a class method is called for a derived class, the derived class\n" +
2159+
"object is passed as the implied first argument.\n" +
2160+
"\n" +
2161+
"Class methods are different than C++ or Java static methods.\n" +
2162+
"If you want those, see the staticmethod builtin.")
2163+
@GenerateNodeFactory
2164+
public abstract static class ClassmethodNode extends PythonBinaryBuiltinNode {
2165+
@Specialization
2166+
Object doObjectIndirect(PythonClass self, @SuppressWarnings("unused") Object callable) {
2167+
return factory().createClassmethod(self);
2168+
}
2169+
}
2170+
2171+
@Builtin(name = STATICMETHOD, fixedNumOfPositionalArgs = 2, constructsClass = PythonBuiltinClassType.PStaticmethod)
2172+
@GenerateNodeFactory
2173+
public abstract static class StaticmethodNode extends PythonBinaryBuiltinNode {
2174+
@Specialization
2175+
Object doObjectIndirect(PythonClass self, @SuppressWarnings("unused") Object callable) {
2176+
return factory().createStaticmethod(self);
2177+
}
2178+
}
21402179
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/floats/FloatBuiltins.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ PNotImplemented doGeneric(Object left, Object right) {
515515
}
516516
}
517517

518-
@Builtin(name = "fromhex", fixedNumOfPositionalArgs = 2)
518+
@Builtin(name = "fromhex", fixedNumOfPositionalArgs = 2, isClassmethod = true)
519519
@GenerateNodeFactory
520520
@TypeSystemReference(PythonArithmeticTypes.class)
521521
public abstract static class FromHexNode extends PythonBuiltinNode {
@@ -1154,7 +1154,7 @@ int trunc(PFloat pValue,
11541154

11551155
}
11561156

1157-
@Builtin(name = __GETFORMAT__, fixedNumOfPositionalArgs = 2)
1157+
@Builtin(name = __GETFORMAT__, fixedNumOfPositionalArgs = 2, isClassmethod = true)
11581158
@GenerateNodeFactory
11591159
@TypeSystemReference(PythonArithmeticTypes.class)
11601160
abstract static class GetFormatNode extends PythonBinaryBuiltinNode {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1627,7 +1627,7 @@ PNotImplemented doGeneric(Object a, Object b) {
16271627
}
16281628
}
16291629

1630-
@Builtin(name = "from_bytes", fixedNumOfPositionalArgs = 3, takesVarArgs = true, keywordArguments = {"signed"})
1630+
@Builtin(name = "from_bytes", fixedNumOfPositionalArgs = 3, takesVarArgs = true, keywordArguments = {"signed"}, isClassmethod = true)
16311631
@GenerateNodeFactory
16321632
@SuppressWarnings("unused")
16331633
@TypeSystemReference(PythonArithmeticTypes.class)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* Copyright (c) 2018, 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.builtins.objects.method;
42+
43+
import static com.oracle.graal.python.nodes.SpecialMethodNames.__GET__;
44+
45+
import java.util.List;
46+
47+
import com.oracle.graal.python.builtins.Builtin;
48+
import com.oracle.graal.python.builtins.CoreFunctions;
49+
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
50+
import com.oracle.graal.python.builtins.PythonBuiltins;
51+
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
52+
import com.oracle.graal.python.builtins.objects.function.PFunction;
53+
import com.oracle.graal.python.builtins.objects.method.ClassmethodBuiltinsFactory.MakeMethodNodeGen;
54+
import com.oracle.graal.python.nodes.PNodeWithContext;
55+
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
56+
import com.oracle.graal.python.nodes.function.PythonBuiltinNode;
57+
import com.oracle.graal.python.nodes.object.GetClassNode;
58+
import com.oracle.truffle.api.dsl.Cached;
59+
import com.oracle.truffle.api.dsl.Fallback;
60+
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
61+
import com.oracle.truffle.api.dsl.NodeFactory;
62+
import com.oracle.truffle.api.dsl.Specialization;
63+
import com.oracle.truffle.api.profiles.BranchProfile;
64+
65+
@CoreFunctions(extendClasses = {PythonBuiltinClassType.PClassmethod})
66+
public class ClassmethodBuiltins extends PythonBuiltins {
67+
68+
@Override
69+
protected List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> getNodeFactories() {
70+
return ClassmethodBuiltinsFactory.getFactories();
71+
}
72+
73+
@Builtin(name = __GET__, minNumOfPositionalArgs = 2, maxNumOfPositionalArgs = 4)
74+
@GenerateNodeFactory
75+
abstract static class CallNode extends PythonBuiltinNode {
76+
@Child MakeMethodNode makeMethod = MakeMethodNode.create();
77+
78+
@Specialization(guards = {"isNoValue(type)"})
79+
protected Object get(PDecoratedMethod self, Object obj, @SuppressWarnings("unused") Object type,
80+
@Cached("create()") GetClassNode getClass,
81+
@Cached("create()") BranchProfile uninitialized) {
82+
return doGet(self, getClass.execute(obj), uninitialized);
83+
}
84+
85+
@Specialization(guards = "!isNoValue(type)")
86+
protected Object doIt(PDecoratedMethod self, @SuppressWarnings("unused") Object obj, Object type,
87+
@Cached("create()") BranchProfile uninitialized) {
88+
return doGet(self, type, uninitialized);
89+
}
90+
91+
private Object doGet(PDecoratedMethod self, Object type, BranchProfile uninitialized) {
92+
Object callable = self.getCallable();
93+
if (callable == null) {
94+
uninitialized.enter();
95+
throw raise(PythonBuiltinClassType.RuntimeError, "uninitialized classmethod object");
96+
}
97+
return makeMethod.execute(type, callable);
98+
}
99+
}
100+
101+
abstract static class MakeMethodNode extends PNodeWithContext {
102+
abstract Object execute(Object self, Object func);
103+
104+
@Specialization
105+
Object method(Object self, PFunction func) {
106+
return factory().createMethod(self, func);
107+
}
108+
109+
@Specialization
110+
Object methodBuiltin(Object self, PBuiltinFunction func) {
111+
return factory().createBuiltinMethod(self, func);
112+
}
113+
114+
@Fallback
115+
Object generic(@SuppressWarnings("unused") Object self, Object func) {
116+
throw raise(PythonBuiltinClassType.NotImplementedError, "classmethods with non-function callables '%p'", func);
117+
}
118+
119+
static MakeMethodNode create() {
120+
return MakeMethodNodeGen.create();
121+
}
122+
}
123+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright (c) 2018, 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.builtins.objects.method;
42+
43+
import static com.oracle.graal.python.nodes.SpecialMethodNames.__INIT__;
44+
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__FUNC__;
45+
46+
import java.util.List;
47+
48+
import com.oracle.graal.python.builtins.Builtin;
49+
import com.oracle.graal.python.builtins.CoreFunctions;
50+
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
51+
import com.oracle.graal.python.builtins.PythonBuiltins;
52+
import com.oracle.graal.python.builtins.objects.PNone;
53+
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
54+
import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
55+
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
56+
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
57+
import com.oracle.truffle.api.dsl.NodeFactory;
58+
import com.oracle.truffle.api.dsl.Specialization;
59+
60+
@CoreFunctions(extendClasses = {PythonBuiltinClassType.PStaticmethod, PythonBuiltinClassType.PClassmethod})
61+
public class DecoratedMethodBuiltins extends PythonBuiltins {
62+
63+
@Override
64+
protected List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> getNodeFactories() {
65+
return DecoratedMethodBuiltinsFactory.getFactories();
66+
}
67+
68+
@Builtin(name = __INIT__, fixedNumOfPositionalArgs = 2)
69+
@GenerateNodeFactory
70+
abstract static class InitNode extends PythonBinaryBuiltinNode {
71+
@Specialization
72+
protected PNone init(PDecoratedMethod self, Object callable) {
73+
self.setCallable(callable);
74+
return PNone.NONE;
75+
}
76+
}
77+
78+
@Builtin(name = __FUNC__, fixedNumOfPositionalArgs = 1, isGetter = true)
79+
@GenerateNodeFactory
80+
abstract static class FuncNode extends PythonUnaryBuiltinNode {
81+
@Specialization
82+
protected Object func(PDecoratedMethod self) {
83+
return self.getCallable();
84+
}
85+
}
86+
}

0 commit comments

Comments
 (0)