diff --git a/ASTree.cpp b/ASTree.cpp index 6635808e1..0a10848f0 100644 --- a/ASTree.cpp +++ b/ASTree.cpp @@ -1576,13 +1576,67 @@ PycRef BuildFromCode(PycRef code, PycModule* mod) ASTFunction::defarg_t defArgs, kwDefArgs; const int defCount = operand & 0xFF; const int kwDefCount = (operand >> 8) & 0xFF; - for (int i = 0; i < defCount; ++i) { - defArgs.push_front(stack.top()); - stack.pop(); - } - for (int i = 0; i < kwDefCount; ++i) { - kwDefArgs.push_front(stack.top()); - stack.pop(); + const int annotationCount = (operand >> 16) & 0x7FFF; + + /* Docs for 3.3 say KW Pairs come first, but reality disagrees */ + if (mod->verCompare(3, 3) <= 0) { + for (int i = 0; i < defCount; ++i) { + defArgs.push_front(stack.top()); + stack.pop(); + } + /* KW Defaults not mentioned in docs, but they come after the positional args */ + for (int i = 0; i < kwDefCount; ++i) { + kwDefArgs.push_front(stack.top()); + stack.pop(); // KW Pair object + stack.pop(); // KW Pair name + } + } else if (mod->verCompare(3, 5) <= 0) { + /* From Py 3.4 there can now be annotation params + The order has also switched so Kwargs come before Pos Args */ + if(annotationCount) { + stack.pop(); // Tuple of param names for annotations + for (int i = 0; i < annotationCount; ++i) { + stack.pop(); // Pop annotation objects and ignore + } + } + for (int i = 0; i < kwDefCount; ++i) { + kwDefArgs.push_front(stack.top()); + stack.pop(); // KW Pair object + stack.pop(); // KW Pair name + } + for (int i = 0; i < defCount; ++i) { + defArgs.push_front(stack.top()); + stack.pop(); + } + } else { + /* From Py 3.6 the operand stopped being an argument count + and changed to a flag that indicates what is represented by + preceding tuples on the stack. Docs for 3.7 are clearer, + docs for 3.6 may have not been correctly updated */ + if(operand & 0x08) { // Cells for free vars to create a closure + stack.pop(); // Ignore these for syntax generation + } + if(operand & 0x04) { // Annotation dict (3.6-9) or string (3.10+) + stack.pop(); // Ignore annotations + } + if(operand & 0x02) { // Kwarg Defaults + PycRef kw_tuple = stack.top(); + stack.pop(); + std::vector> kw_values = kw_tuple.cast()->values(); + + for(const PycRef& kw : kw_values) { + kwDefArgs.push_front(kw); + } + } + if(operand & 0x01) { // Positional Defaults (including positional-or-KW args) + PycRef pos_tuple = stack.top(); + stack.pop(); + std::vector> pos_values = pos_tuple.cast()->object().cast()->values(); + + for(const PycRef& pos : pos_values) { + defArgs.push_back(new ASTObject(pos)); + } + } } stack.push(new ASTFunction(fun_code, defArgs, kwDefArgs)); } diff --git a/tests/compiled/test_functions_py3.3.0.pyc b/tests/compiled/test_functions_py3.3.0.pyc index f29f67148..56b7c9f80 100644 Binary files a/tests/compiled/test_functions_py3.3.0.pyc and b/tests/compiled/test_functions_py3.3.0.pyc differ diff --git a/tests/compiled/test_functions_py3.3.4.pyc b/tests/compiled/test_functions_py3.3.4.pyc index 6c0fe16ae..a8aff048e 100644 Binary files a/tests/compiled/test_functions_py3.3.4.pyc and b/tests/compiled/test_functions_py3.3.4.pyc differ diff --git a/tests/xfail/test_functions_py3.3.7.pyc b/tests/compiled/test_functions_py3.3.7.pyc similarity index 63% rename from tests/xfail/test_functions_py3.3.7.pyc rename to tests/compiled/test_functions_py3.3.7.pyc index 763ba8e6b..b059fba90 100644 Binary files a/tests/xfail/test_functions_py3.3.7.pyc and b/tests/compiled/test_functions_py3.3.7.pyc differ diff --git a/tests/input/test_functions_py3.py b/tests/input/test_functions_py3.py index f11c75c78..502c13f73 100644 --- a/tests/input/test_functions_py3.py +++ b/tests/input/test_functions_py3.py @@ -57,3 +57,6 @@ def x7c(foo = 1, *, bar, **kwargs): def x7d(foo = 1, *, bar = 2, **kwargs): pass + +def x7e(foo = 1, *, bar = 2, baz = 3, **kwargs): + pass diff --git a/tests/tokenized/test_functions_py3.txt b/tests/tokenized/test_functions_py3.txt index 8308e0b70..0cfae7a5d 100644 --- a/tests/tokenized/test_functions_py3.txt +++ b/tests/tokenized/test_functions_py3.txt @@ -34,18 +34,50 @@ def x4c ( foo , bar = 1 , bla = 2 , * args , ** kwargs ) : pass -def x5a ( * , bar = 1 ) : +def x5a ( * , bar ) : pass -def x5b ( * , bar = 1 , ** kwargs ) : +def x5b ( * , bar = 1 ) : pass -def x6a ( foo , * , bar = 1 ) : +def x5c ( * , bar = 1 , ** kwargs ) : pass -def x7a ( foo , * , bar = 1 , ** kwargs ) : +def x6a ( foo , * , bar ) : + +pass + +def x6b ( foo , * , bar = 1 ) : + +pass + +def x6c ( foo = 1 , * , bar ) : + +pass + +def x6d ( foo = 1 , * , bar = 2 ) : + +pass + +def x7a ( foo , * , bar , ** kwargs ) : + +pass + +def x7b ( foo , * , bar = 1 , ** kwargs ) : + +pass + +def x7c ( foo = 1 , * , bar , ** kwargs ) : + +pass + +def x7d ( foo = 1 , * , bar = 2 , ** kwargs ) : + +pass + +def x7e ( foo = 1 , * , bar = 2 , baz = 3 , ** kwargs ) : pass