Skip to content

Commit b470802

Browse files
committed
complete support for function.__defaults__ and function construction
- added relevant unittest
1 parent 9ec3f8b commit b470802

File tree

9 files changed

+98
-20
lines changed

9 files changed

+98
-20
lines changed

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

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

40+
4041
def test_name():
4142
def foo():
4243
pass
@@ -52,3 +53,25 @@ def foo():
5253
assert "__name__ must be set to a string object" in str(e)
5354
else:
5455
assert False
56+
57+
58+
def f(a, b, c=10, *args, **kwargs):
59+
return a, b, c, args, kwargs
60+
61+
62+
def f2(a=f(1, 2), b=10):
63+
return a, b
64+
65+
66+
def test_defaults():
67+
assert f.__defaults__ == (10,)
68+
assert f2.__defaults__ == ((1, 2, 10, (), {}), 10)
69+
70+
71+
def test_constructor():
72+
import types
73+
func_copy = types.FunctionType(f.__code__, f.__globals__, f.__name__, f.__defaults__, f.__closure__)
74+
75+
assert func_copy(1, 2) == (1, 2, 10, (), {})
76+
assert func_copy(1, 2, 3) == (1, 2, 3, (), {})
77+
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: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1480,15 +1480,24 @@ public PZip zip(PythonClass cls, Object[] args,
14801480
PythonBuiltinClassType.PGeneratorFunction}, isPublic = false)
14811481
@GenerateNodeFactory
14821482
public abstract static class FunctionNode extends PythonBuiltinNode {
1483-
14841483
@Specialization
14851484
public PFunction function(PythonClass cls, PCode code, PDict globals, String name, @SuppressWarnings("unused") PNone defaultArgs, @SuppressWarnings("unused") PNone closure) {
14861485
return factory().createFunction(name, cls.getName(), code.getArity(), code.getRootCallTarget(), code.getFrameDescriptor(), globals, null);
14871486
}
14881487

14891488
@Specialization
1490-
public PFunction function(PythonClass cls, PCode code, PDict globals, String name, @SuppressWarnings("unused") PTuple defaultArgs, PTuple closure) {
1491-
return factory().createFunction(name, cls.getName(), code.getArity(), code.getRootCallTarget(), code.getFrameDescriptor(), globals, (PCell[])closure.getArray());
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+
}
1492+
1493+
@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());
14921501
}
14931502

14941503
@Fallback

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

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ private void extractArgStats() {
268268
this.argcount = readIndexedArgumentNodes.size();
269269
this.kwonlyargcount = 0;
270270

271-
for(int i = 0; i < readKeywordNodes.size(); i++) {
271+
for (int i = 0; i < readKeywordNodes.size(); i++) {
272272
ReadKeywordNode kwNode = readKeywordNodes.get(i);
273273
keywordNames[i++] = new Arity.KeywordName(kwNode.getName(), kwNode.isRequired());
274274
if (!kwNode.canBePositional()) {
@@ -392,7 +392,7 @@ public Object getLnotab() {
392392
return lnotab;
393393
}
394394

395-
public Arity.KeywordName[] getKeywordNames() {
395+
private Arity.KeywordName[] getKeywordNames() {
396396
if (keywordNames == null && rootNode != null) {
397397
extractArgStats();
398398
}
@@ -414,14 +414,26 @@ public boolean takesVarKeywordArgs() {
414414
return (flags & (1 << FLAG_POS_VAR_KW_ARGS)) > 0;
415415
}
416416

417+
private int getMinNumOfPositionalArgs() {
418+
int defaultKwNames = 0;
419+
for (Arity.KeywordName kwName : getKeywordNames()) {
420+
defaultKwNames += (kwName.required) ? 0 : 1;
421+
}
422+
return getArgcount() - defaultKwNames;
423+
}
424+
425+
private int getMaxNumOfPositionalArgs() {
426+
return this.getArgcount();
427+
}
428+
417429
public Arity getArity() {
418-
return new Arity(this.getName(), this.getArgcount(), this.getArgcount() + this.getKwonlyargcount(), this.takesVarKeywordArgs(), this.takesVarArgs(), this.getKeywordNames());
430+
return new Arity(this.getName(), this.getMinNumOfPositionalArgs(), this.getMaxNumOfPositionalArgs(), this.takesVarKeywordArgs(), this.takesVarArgs(), this.getKeywordNames());
419431
}
420432

421433
@TruffleBoundary
422434
private FrameDescriptor createFrameDescriptor() {
423435
FrameDescriptor fd = new FrameDescriptor();
424-
for (Object identifier: varnames) {
436+
for (Object identifier : varnames) {
425437
fd.addFrameSlot(identifier);
426438
}
427439
return fd;
@@ -430,7 +442,7 @@ private FrameDescriptor createFrameDescriptor() {
430442
public FrameDescriptor getFrameDescriptor() {
431443
if (frameDescriptor == null) {
432444
if (rootNode != null) {
433-
frameDescriptor = rootNode.getFrameDescriptor();
445+
frameDescriptor = rootNode.getFrameDescriptor();
434446
} else {
435447
frameDescriptor = createFrameDescriptor();
436448
}

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

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import static com.oracle.graal.python.runtime.exception.PythonErrorType.AttributeError;
4040
import static com.oracle.graal.python.runtime.exception.PythonErrorType.NotImplementedError;
4141

42+
import java.util.ArrayList;
4243
import java.util.List;
4344

4445
import com.oracle.graal.python.builtins.Builtin;
@@ -55,6 +56,7 @@
5556
import com.oracle.graal.python.builtins.objects.object.PythonObject;
5657
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
5758
import com.oracle.graal.python.nodes.argument.CreateArgumentsNode;
59+
import com.oracle.graal.python.nodes.argument.ReadKeywordNode;
5860
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
5961
import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode;
6062
import com.oracle.graal.python.nodes.call.CallDispatchNode;
@@ -65,12 +67,14 @@
6567
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
6668
import com.oracle.graal.python.nodes.subscript.GetItemNode;
6769
import com.oracle.truffle.api.CompilerDirectives;
70+
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
6871
import com.oracle.truffle.api.dsl.Cached;
6972
import com.oracle.truffle.api.dsl.Fallback;
7073
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
7174
import com.oracle.truffle.api.dsl.NodeFactory;
7275
import com.oracle.truffle.api.dsl.Specialization;
7376
import com.oracle.truffle.api.frame.VirtualFrame;
77+
import com.oracle.truffle.api.nodes.NodeUtil;
7478
import com.oracle.truffle.api.profiles.ConditionProfile;
7579

7680
@CoreFunctions(extendClasses = {PythonBuiltinClassType.PFunction, PythonBuiltinClassType.PBuiltinFunction})
@@ -263,17 +267,29 @@ Object builtinCode(PBuiltinFunction self, Object none) {
263267
@Builtin(name = __DEFAULTS__, minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true)
264268
@GenerateNodeFactory
265269
public abstract static class GetDefaultsNode extends PythonBinaryBuiltinNode {
266-
private Object[] getDefaultsAndInitIfNotSet(PFunction function) {
267-
// TODO: add support for __DEFAULTS__ (init from actual default values ...)
268-
return function.getDefaults();
270+
protected final ConditionProfile nullDefaultsProfile = ConditionProfile.createBinaryProfile();
271+
272+
@TruffleBoundary
273+
private Object[] extractDefaults(PFunction function) {
274+
List<Object> defaultValues = new ArrayList<>();
275+
List<ReadKeywordNode> readKeywordNodes = NodeUtil.findAllNodeInstances(function.getFunctionRootNode(), ReadKeywordNode.class);
276+
for (ReadKeywordNode readKeywordNode : readKeywordNodes) {
277+
Object defaultValue = readKeywordNode.getDefaultValue();
278+
if (defaultValue != null) {
279+
defaultValues.add(defaultValue);
280+
}
281+
}
282+
return defaultValues.toArray();
269283
}
270284

271285
private Object getDefaults(PFunction function) {
272-
Object[] defaultVals = getDefaultsAndInitIfNotSet(function);
273-
if (defaultVals == null) {
274-
return PNone.NONE;
286+
Object[] defaults = function.getDefaults();
287+
if (nullDefaultsProfile.profile(defaults == null)) {
288+
defaults = extractDefaults(function);
275289
}
276-
return factory().createTuple(defaultVals);
290+
291+
assert defaults != null;
292+
return (defaults.length == 0) ? PNone.NONE : factory().createTuple(defaults);
277293
}
278294

279295
@Specialization

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,11 @@ public class Arity {
5151
@CompilationFinal(dimensions = 1) private final KeywordName[] keywordNames;
5252

5353
public Arity(String functionName, int minNumOfPositionalArgs, int maxNumOfPositionalArgs,
54-
boolean takesVarKeywordArgs, boolean takesVarArgs,
55-
KeywordName[] keywordNames) {
54+
boolean takesVarKeywordArgs, boolean takesVarArgs,
55+
KeywordName[] keywordNames) {
5656
this(functionName, minNumOfPositionalArgs, maxNumOfPositionalArgs,
57-
takesVarKeywordArgs, takesVarArgs, false,
58-
null, keywordNames);
57+
takesVarKeywordArgs, takesVarArgs, false,
58+
null, keywordNames);
5959
}
6060

6161
public Arity(String functionName, int minNumOfPositionalArgs, int maxNumOfPositionalArgs,

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ public class PFunction extends PythonObject implements PythonCallable {
5454
private Object[] defaults;
5555

5656
public PFunction(PythonClass clazz, String name, String enclosingClassName, Arity arity, RootCallTarget callTarget, FrameDescriptor frameDescriptor, PythonObject globals, PCell[] closure) {
57+
this(clazz, name, enclosingClassName, arity, callTarget, frameDescriptor, globals, null, closure);
58+
}
59+
60+
public PFunction(PythonClass clazz, String name, String enclosingClassName, Arity arity, RootCallTarget callTarget, FrameDescriptor frameDescriptor, PythonObject globals, Object[] defaults,
61+
PCell[] closure) {
5762
super(clazz);
5863
this.name = name;
5964
this.isStatic = name.equals(SpecialMethodNames.__NEW__);
@@ -62,6 +67,7 @@ public PFunction(PythonClass clazz, String name, String enclosingClassName, Arit
6267
this.callTarget = callTarget;
6368
this.frameDescriptor = frameDescriptor;
6469
this.globals = globals;
70+
this.defaults = defaults;
6571
this.closure = closure;
6672
addDefaultConstants(this.getStorage(), name, enclosingClassName);
6773
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/argument/ReadDefaultArgumentNode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public void setValue(Object value) {
4141
}
4242

4343
public Object execute() {
44-
assert value != null;
44+
assert value != null : "ReadDefaultArgumentNode: value cannot be null!";
4545
return value;
4646
}
4747

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/argument/ReadKeywordNode.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,4 +144,11 @@ private Object determineKeyword(VirtualFrame frame, PKeyword keyword) {
144144
public String getName() {
145145
return name;
146146
}
147+
148+
public Object getDefaultValue() {
149+
if (defaultNode != null) {
150+
return defaultNode.execute();
151+
}
152+
return null;
153+
}
147154
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PythonObjectFactory.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,11 @@ public PFunction createFunction(String name, String enclosingClassName, Arity ar
359359
return trace(new PFunction(lookupClass(PythonBuiltinClassType.PFunction), name, enclosingClassName, arity, callTarget, frameDescriptor, globals, closure));
360360
}
361361

362+
public PFunction createFunction(String name, String enclosingClassName, Arity arity, RootCallTarget callTarget, FrameDescriptor frameDescriptor, PythonObject globals, Object[] defaults,
363+
PCell[] closure) {
364+
return trace(new PFunction(lookupClass(PythonBuiltinClassType.PFunction), name, enclosingClassName, arity, callTarget, frameDescriptor, globals, defaults, closure));
365+
}
366+
362367
public PBuiltinFunction createBuiltinFunction(String name, PythonClass type, Arity arity, RootCallTarget callTarget) {
363368
return trace(new PBuiltinFunction(lookupClass(PythonBuiltinClassType.PBuiltinFunction), name, type, arity, callTarget));
364369
}

0 commit comments

Comments
 (0)