Skip to content

Commit 0b3229f

Browse files
committed
[GR-11623] function constructor support
PullRequest: graalpython/186
2 parents 88771f3 + 07f631f commit 0b3229f

File tree

11 files changed

+258
-15
lines changed

11 files changed

+258
-15
lines changed

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@
3737
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3838
# SOFTWARE.
3939

40+
41+
def assert_raises(err, fn, *args, **kwargs):
42+
raised = False
43+
try:
44+
fn(*args, **kwargs)
45+
except err:
46+
raised = True
47+
assert raised
48+
49+
4050
def test_name():
4151
def foo():
4252
pass
@@ -52,3 +62,39 @@ def foo():
5262
assert "__name__ must be set to a string object" in str(e)
5363
else:
5464
assert False
65+
66+
67+
def f(a, b, c=10, *args, **kwargs):
68+
return a, b, c, args, kwargs
69+
70+
71+
def f2(a=f(1, 2), b=10):
72+
return a, b
73+
74+
75+
class MyClass(object):
76+
def __init__(self, x = 10):
77+
pass
78+
79+
80+
def test_defaults():
81+
assert f.__defaults__ == (10,)
82+
assert f2.__defaults__ == ((1, 2, 10, (), {}), 10)
83+
84+
85+
def test_defaults_method():
86+
obj = MyClass()
87+
assert obj.__init__.__defaults__ == (10,)
88+
89+
def assgn():
90+
obj.__init__.__defaults__ = (12,)
91+
assert_raises(AttributeError, assgn)
92+
93+
94+
def test_constructor():
95+
import types
96+
func_copy = types.FunctionType(f.__code__, f.__globals__, f.__name__, f.__defaults__, f.__closure__)
97+
98+
assert func_copy(1, 2) == (1, 2, 10, (), {})
99+
assert func_copy(1, 2, 3) == (1, 2, 3, (), {})
100+
assert func_copy(1, 2, 3, 4, 5, x=2) == (1, 2, 3, (4, 5), {'x': 2})

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

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,10 @@
6868
import com.oracle.graal.python.builtins.objects.bytes.BytesUtils;
6969
import com.oracle.graal.python.builtins.objects.bytes.PBytes;
7070
import com.oracle.graal.python.builtins.objects.bytes.PIBytesLike;
71+
import com.oracle.graal.python.builtins.objects.cell.PCell;
7172
import com.oracle.graal.python.builtins.objects.cext.CExtNodes;
7273
import com.oracle.graal.python.builtins.objects.cext.PythonNativeClass;
74+
import com.oracle.graal.python.builtins.objects.code.PCode;
7375
import com.oracle.graal.python.builtins.objects.common.HashingStorage.DictEntry;
7476
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes;
7577
import com.oracle.graal.python.builtins.objects.common.PHashingCollection;
@@ -1478,11 +1480,30 @@ public PZip zip(PythonClass cls, Object[] args,
14781480
PythonBuiltinClassType.PGeneratorFunction}, isPublic = false)
14791481
@GenerateNodeFactory
14801482
public abstract static class FunctionNode extends PythonBuiltinNode {
1483+
@Specialization
1484+
public PFunction function(PythonClass cls, PCode code, PDict globals, String name, @SuppressWarnings("unused") PNone defaultArgs, @SuppressWarnings("unused") PNone closure) {
1485+
return factory().createFunction(name, cls.getName(), code.getArity(), code.getRootCallTarget(), code.getFrameDescriptor(), globals, null);
1486+
}
1487+
1488+
@Specialization
1489+
public PFunction function(PythonClass cls, PCode code, PDict globals, String name, @SuppressWarnings("unused") PNone defaultArgs, PTuple closure) {
1490+
return factory().createFunction(name, cls.getName(), code.getArity(), code.getRootCallTarget(), code.getFrameDescriptor(), globals, (PCell[]) closure.getArray());
1491+
}
14811492

14821493
@Specialization
1494+
public PFunction function(PythonClass cls, PCode code, PDict globals, String name, PTuple defaultArgs, @SuppressWarnings("unused") PNone closure) {
1495+
return factory().createFunction(name, cls.getName(), code.getArity(), code.getRootCallTarget(), code.getFrameDescriptor(), globals, defaultArgs.getArray(), null);
1496+
}
1497+
1498+
@Specialization
1499+
public PFunction function(PythonClass cls, PCode code, PDict globals, String name, PTuple defaultArgs, PTuple closure) {
1500+
return factory().createFunction(name, cls.getName(), code.getArity(), code.getRootCallTarget(), code.getFrameDescriptor(), globals, defaultArgs.getArray(), (PCell[]) closure.getArray());
1501+
}
1502+
1503+
@Fallback
14831504
@SuppressWarnings("unused")
1484-
public PFunction function(Object cls, Object code, PDict globals, String name, PTuple defaultArgs, PTuple closure) {
1485-
throw raise(NotImplementedError, "function construction not implemented");
1505+
public PFunction function(Object cls, Object code, Object globals, Object name, Object defaultArgs, Object closure) {
1506+
throw raise(TypeError, "function construction not supported for (%p, %p, %p, %p, %p, %p)", cls, code, globals, name, defaultArgs, closure);
14861507
}
14871508
}
14881509

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/PCode.java

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import java.util.List;
4646
import java.util.Set;
4747

48+
import com.oracle.graal.python.builtins.objects.function.Arity;
4849
import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject;
4950
import com.oracle.graal.python.builtins.objects.type.PythonClass;
5051
import com.oracle.graal.python.nodes.ModuleRootNode;
@@ -57,12 +58,19 @@
5758
import com.oracle.graal.python.nodes.generator.GeneratorFunctionRootNode;
5859
import com.oracle.graal.python.runtime.PythonCore;
5960
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
61+
import com.oracle.truffle.api.RootCallTarget;
62+
import com.oracle.truffle.api.Truffle;
63+
import com.oracle.truffle.api.frame.FrameDescriptor;
6064
import com.oracle.truffle.api.nodes.Node;
6165
import com.oracle.truffle.api.nodes.NodeUtil;
6266
import com.oracle.truffle.api.nodes.RootNode;
6367
import com.oracle.truffle.api.source.SourceSection;
6468

6569
public class PCode extends PythonBuiltinObject {
70+
private final long FLAG_POS_GENERATOR = 5;
71+
private final long FLAG_POS_VAR_ARGS = 2;
72+
private final long FLAG_POS_VAR_KW_ARGS = 3;
73+
6674
private final RootNode rootNode;
6775
private final PythonCore core;
6876

@@ -101,6 +109,11 @@ public class PCode extends PythonBuiltinObject {
101109
// tuple of names of cell variables (referenced by containing scopes)
102110
private Object[] cellvars;
103111

112+
// internal cache for keyword names
113+
private Arity.KeywordName[] keywordNames;
114+
// internal cache for the FrameDescriptor
115+
private FrameDescriptor frameDescriptor;
116+
104117
public PCode(PythonClass cls, RootNode rootNode, PythonCore core) {
105118
super(cls);
106119
this.rootNode = rootNode;
@@ -223,17 +236,17 @@ private void extractArgStats() {
223236
this.flags = 0;
224237
RootNode funcRootNode = rootNode;
225238
if (funcRootNode instanceof GeneratorFunctionRootNode) {
226-
flags |= (1 << 5);
239+
flags |= (1 << FLAG_POS_GENERATOR);
227240
funcRootNode = ((GeneratorFunctionRootNode) funcRootNode).getFunctionRootNode();
228241
}
229242

230243
// 0x04 - *arguments
231244
if (NodeUtil.findAllNodeInstances(funcRootNode, ReadVarArgsNode.class).size() == 1) {
232-
flags |= (1 << 2);
245+
flags |= (1 << FLAG_POS_VAR_ARGS);
233246
}
234247
// 0x08 - **keywords
235248
if (NodeUtil.findAllNodeInstances(funcRootNode, ReadVarKeywordsNode.class).size() == 1) {
236-
flags |= (1 << 3);
249+
flags |= (1 << FLAG_POS_VAR_KW_ARGS);
237250
}
238251

239252
this.freevars = extractFreeVars(rootNode);
@@ -242,6 +255,7 @@ private void extractArgStats() {
242255
Set<String> cellVarsSet = asSet((String[]) cellvars);
243256

244257
List<ReadKeywordNode> readKeywordNodes = NodeUtil.findAllNodeInstances(funcRootNode, ReadKeywordNode.class);
258+
keywordNames = new Arity.KeywordName[readKeywordNodes.size()];
245259
List<ReadIndexedArgumentNode> readIndexedArgumentNodes = NodeUtil.findAllNodeInstances(funcRootNode, ReadIndexedArgumentNode.class);
246260

247261
Set<String> kwNames = getKeywordArgumentNames(readKeywordNodes);
@@ -254,7 +268,9 @@ private void extractArgStats() {
254268
this.argcount = readIndexedArgumentNodes.size();
255269
this.kwonlyargcount = 0;
256270

257-
for (ReadKeywordNode kwNode : readKeywordNodes) {
271+
for (int i = 0; i < readKeywordNodes.size(); i++) {
272+
ReadKeywordNode kwNode = readKeywordNodes.get(i);
273+
keywordNames[i++] = new Arity.KeywordName(kwNode.getName(), kwNode.isRequired());
258274
if (!kwNode.canBePositional()) {
259275
kwonlyargcount++;
260276
}
@@ -375,4 +391,67 @@ public Object getNames() {
375391
public Object getLnotab() {
376392
return lnotab;
377393
}
394+
395+
private Arity.KeywordName[] getKeywordNames() {
396+
if (keywordNames == null && rootNode != null) {
397+
extractArgStats();
398+
}
399+
return keywordNames;
400+
}
401+
402+
public boolean isGenerator() {
403+
return (getFlags() & (1 << FLAG_POS_GENERATOR)) > 0;
404+
}
405+
406+
public boolean takesVarArgs() {
407+
return (getFlags() & (1 << FLAG_POS_VAR_ARGS)) > 0;
408+
}
409+
410+
public boolean takesVarKeywordArgs() {
411+
return (getFlags() & (1 << FLAG_POS_VAR_KW_ARGS)) > 0;
412+
}
413+
414+
private int getMinNumOfPositionalArgs() {
415+
int defaultKwNames = 0;
416+
for (Arity.KeywordName kwName : getKeywordNames()) {
417+
defaultKwNames += (kwName.required) ? 0 : 1;
418+
}
419+
return getArgcount() - defaultKwNames;
420+
}
421+
422+
private int getMaxNumOfPositionalArgs() {
423+
return this.getArgcount();
424+
}
425+
426+
public Arity getArity() {
427+
return new Arity(this.getName(), this.getMinNumOfPositionalArgs(), this.getMaxNumOfPositionalArgs(), this.takesVarKeywordArgs(), this.takesVarArgs(), this.getKeywordNames());
428+
}
429+
430+
@TruffleBoundary
431+
private FrameDescriptor createFrameDescriptor() {
432+
FrameDescriptor fd = new FrameDescriptor();
433+
for (Object identifier : varnames) {
434+
fd.addFrameSlot(identifier);
435+
}
436+
return fd;
437+
}
438+
439+
public FrameDescriptor getFrameDescriptor() {
440+
if (frameDescriptor == null) {
441+
if (rootNode != null) {
442+
frameDescriptor = rootNode.getFrameDescriptor();
443+
} else {
444+
frameDescriptor = createFrameDescriptor();
445+
}
446+
}
447+
return frameDescriptor;
448+
}
449+
450+
@TruffleBoundary
451+
public RootCallTarget getRootCallTarget() {
452+
if (rootNode != null) {
453+
return Truffle.getRuntime().createCallTarget(rootNode);
454+
}
455+
return null;
456+
}
378457
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/AbstractFunctionBuiltins.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
import com.oracle.graal.python.nodes.call.CallDispatchNode;
5959
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
6060
import com.oracle.graal.python.nodes.function.PythonBuiltinNode;
61+
import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
6162
import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode;
6263
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
6364
import com.oracle.graal.python.nodes.subscript.GetItemNode;
@@ -232,7 +233,7 @@ Object getModule(PBuiltinFunction self, Object value) {
232233

233234
@Builtin(name = __CODE__, minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true)
234235
@GenerateNodeFactory
235-
public abstract static class GetCodeNode extends PythonBuiltinNode {
236+
public abstract static class GetCodeNode extends PythonBinaryBuiltinNode {
236237
@Specialization(guards = {"!isBuiltinFunction(self)", "isNoValue(none)"})
237238
Object getCode(PFunction self, @SuppressWarnings("unused") PNone none,
238239
@Cached("createBinaryProfile()") ConditionProfile hasCodeProfile) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/Arity.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ public class Arity {
5050
@CompilationFinal(dimensions = 1) private final String[] parameterIds;
5151
@CompilationFinal(dimensions = 1) private final KeywordName[] keywordNames;
5252

53+
public Arity(String functionName, int minNumOfPositionalArgs, int maxNumOfPositionalArgs,
54+
boolean takesVarKeywordArgs, boolean takesVarArgs,
55+
KeywordName[] keywordNames) {
56+
this(functionName, minNumOfPositionalArgs, maxNumOfPositionalArgs,
57+
takesVarKeywordArgs, takesVarArgs, false,
58+
null, keywordNames);
59+
}
60+
5361
public Arity(String functionName, int minNumOfPositionalArgs, int maxNumOfPositionalArgs,
5462
boolean takesVarKeywordArgs, boolean takesVarArgs,
5563
List<KeywordName> keywordNames) {
@@ -85,13 +93,11 @@ public Arity(String functionName, int minNumOfPositionalArgs, int maxNumOfPositi
8593
}
8694

8795
public static Arity createOneArgumentWithVarKwArgs(String functionName) {
88-
return new Arity(functionName, 1, 1,
89-
true, false, null);
96+
return new Arity(functionName, 1, 1, true, false, (KeywordName[]) null);
9097
}
9198

9299
public static Arity createVarArgsAndKwArgsOnly(String functionName) {
93-
return new Arity(functionName, 0, 0,
94-
true, true, null);
100+
return new Arity(functionName, 0, 0, true, true, (KeywordName[]) null);
95101
}
96102

97103
@TruffleBoundary

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/FunctionBuiltins.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,22 @@
2626

2727
package com.oracle.graal.python.builtins.objects.function;
2828

29+
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__DEFAULTS__;
2930
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__NAME__;
3031
import static com.oracle.graal.python.nodes.SpecialMethodNames.__REPR__;
3132

33+
import java.util.ArrayList;
3234
import java.util.List;
3335

3436
import com.oracle.graal.python.builtins.Builtin;
3537
import com.oracle.graal.python.builtins.CoreFunctions;
3638
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
3739
import com.oracle.graal.python.builtins.PythonBuiltins;
3840
import com.oracle.graal.python.builtins.objects.PNone;
41+
import com.oracle.graal.python.builtins.objects.function.FunctionBuiltinsFactory.GetFunctionDefaultsNodeFactory;
3942
import com.oracle.graal.python.builtins.objects.str.PString;
43+
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
44+
import com.oracle.graal.python.nodes.argument.ReadKeywordNode;
4045
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
4146
import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode;
4247
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
@@ -52,6 +57,8 @@
5257
import com.oracle.truffle.api.dsl.NodeFactory;
5358
import com.oracle.truffle.api.dsl.Specialization;
5459
import com.oracle.truffle.api.dsl.TypeSystemReference;
60+
import com.oracle.truffle.api.nodes.NodeUtil;
61+
import com.oracle.truffle.api.profiles.ConditionProfile;
5562

5663
@CoreFunctions(extendClasses = PythonBuiltinClassType.PFunction)
5764
public class FunctionBuiltins extends PythonBuiltins {
@@ -128,4 +135,47 @@ Object setName(Object self, Object value) {
128135
}
129136
}
130137

138+
@Builtin(name = __DEFAULTS__, minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true)
139+
@GenerateNodeFactory
140+
public abstract static class GetFunctionDefaultsNode extends PythonBinaryBuiltinNode {
141+
protected final ConditionProfile nullDefaultsProfile = ConditionProfile.createBinaryProfile();
142+
143+
@TruffleBoundary
144+
private static Object[] extractDefaults(PFunction function) {
145+
List<Object> defaultValues = new ArrayList<>();
146+
List<ReadKeywordNode> readKeywordNodes = NodeUtil.findAllNodeInstances(function.getFunctionRootNode(), ReadKeywordNode.class);
147+
for (ReadKeywordNode readKeywordNode : readKeywordNodes) {
148+
Object defaultValue = readKeywordNode.getDefaultValue();
149+
if (defaultValue != null) {
150+
defaultValues.add(defaultValue);
151+
}
152+
}
153+
return defaultValues.toArray();
154+
}
155+
156+
private Object getDefaults(PFunction function) {
157+
Object[] defaults = function.getDefaults();
158+
if (nullDefaultsProfile.profile(defaults == null)) {
159+
defaults = extractDefaults(function);
160+
}
161+
162+
assert defaults != null;
163+
return (defaults.length == 0) ? PNone.NONE : factory().createTuple(defaults);
164+
}
165+
166+
@Specialization
167+
Object defaults(PFunction self, @SuppressWarnings("unused") PNone defaults) {
168+
return getDefaults(self);
169+
}
170+
171+
@Specialization
172+
Object defaults(PFunction self, PTuple defaults) {
173+
self.setDefaults(defaults.getArray());
174+
return PNone.NONE;
175+
}
176+
177+
public static GetFunctionDefaultsNode create() {
178+
return GetFunctionDefaultsNodeFactory.create();
179+
}
180+
}
131181
}

0 commit comments

Comments
 (0)