diff --git a/ASTree.cpp b/ASTree.cpp index 6635808e1..ef92bc05c 100644 --- a/ASTree.cpp +++ b/ASTree.cpp @@ -1559,13 +1559,14 @@ PycRef BuildFromCode(PycRef code, PycModule* mod) case Pyc::LOAD_NAME_A: stack.push(new ASTName(code->getName(operand))); break; + case Pyc::MAKE_CLOSURE_A: + case Pyc::MAKE_FUNCTION: case Pyc::MAKE_FUNCTION_A: { PycRef fun_code = stack.top(); stack.pop(); - - /* Test for the qualified name of the function (at TOS) */ + // Test for the qualified name of the function (at TOS) int tos_type = fun_code.cast()->object().type(); if (tos_type != PycObject::TYPE_CODE && tos_type != PycObject::TYPE_CODE2) { @@ -1576,17 +1577,164 @@ 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(); + const int annotationCount = (operand >> 16) & 0x7FFF; + + // Python 3.3 and below + 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 + } } - for (int i = 0; i < kwDefCount; ++i) { - kwDefArgs.push_front(stack.top()); - stack.pop(); + // Python 3.4-3.5 + else if (mod->verCompare(3, 5) <= 0) { + // From Py 3.4: annotations and order change (kw first) + 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(); + } + } + // Python 3.6-3.9 (flag mask, annotation dict) + else if (mod->verCompare(3, 9) <= 0) { + 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) + 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)); + } + } + } + // Python 3.10-3.13 (annotation can be dict or string, otherwise same) + else if (mod->verCompare(3, 13) <= 0) { + if (operand & 0x08) { // Cells for free vars to create a closure + stack.pop(); // Ignore these for syntax generation + } + if (operand & 0x04) { // Annotation dict (3.10-13) or string (PEP 563) + 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)); + } + } } + // Python 3.14+ (assume new flag 0x10 for environment dict, otherwise same as 3.13) + else if (mod->verCompare(3, 14) <= 0) { + if (operand & 0x10) { // Hypothetical: function environment variables + stack.pop(); // Ignore environment dict + } + if (operand & 0x08) { // Cells for free vars to create a closure + stack.pop(); // Ignore these for syntax generation + } + if (operand & 0x04) { // Annotation dict or string + 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)); + } + } + } + // Unknown future versions: fallback, try 3.14 logic + else { + if (operand & 0x10) { + stack.pop(); + } + if (operand & 0x08) { + stack.pop(); + } + if (operand & 0x04) { + stack.pop(); + } + if (operand & 0x02) { + 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) { + 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)); } break; + case Pyc::SET_FUNCTION_ATTRIBUTE_A: + { + PycRef value = stack.top(); stack.pop(); + PycRef func = stack.top(); stack.pop(); + + // Currently we can't store the attribute (no AST support yet), + // so just discard 'value' and keep function on stack. + (void)value; // suppress unused warning + + stack.push(func); + } + break; + + case Pyc::NOP: break; case Pyc::POP_BLOCK: diff --git a/tests/compiled/test_functions_3_13.3.13.pyc b/tests/compiled/test_functions_3_13.3.13.pyc new file mode 100644 index 000000000..66a606e7d Binary files /dev/null and b/tests/compiled/test_functions_3_13.3.13.pyc differ diff --git a/tests/input/test_functions_3_13.py b/tests/input/test_functions_3_13.py new file mode 100644 index 000000000..84a6ef519 --- /dev/null +++ b/tests/input/test_functions_3_13.py @@ -0,0 +1,4 @@ +def top(): + def inner(c): + return c * 2 + return inner diff --git a/tests/tokenized/test_functions_3_13.txt b/tests/tokenized/test_functions_3_13.txt new file mode 100644 index 000000000..24c63db19 --- /dev/null +++ b/tests/tokenized/test_functions_3_13.txt @@ -0,0 +1,7 @@ +def top ( ) : + +def inner ( c ) : + +return c * 2 + +return inner