diff --git a/ASTNode.h b/ASTNode.h index 9a621b0..22b90e1 100644 --- a/ASTNode.h +++ b/ASTNode.h @@ -18,6 +18,7 @@ public: NODE_COMPREHENSION, NODE_LOADBUILDCLASS, NODE_AWAITABLE, NODE_FORMATTEDVALUE, NODE_JOINEDSTR, NODE_CONST_MAP, NODE_ANNOTATED_VAR, NODE_CHAINSTORE, NODE_TERNARY, + NODE_KW_NAMES_MAP, // Empty node types NODE_LOCALS, @@ -394,6 +395,23 @@ private: map_t m_values; }; +class ASTKwNamesMap : public ASTNode { +public: + typedef std::list, PycRef>> map_t; + + ASTKwNamesMap() : ASTNode(NODE_KW_NAMES_MAP) { } + + void add(PycRef key, PycRef value) + { + m_values.emplace_back(std::move(key), std::move(value)); + } + + const map_t& values() const { return m_values; } + +private: + map_t m_values; +}; + class ASTConstMap : public ASTNode { public: typedef std::vector> values_t; diff --git a/ASTree.cpp b/ASTree.cpp index 4245a69..63a894e 100644 --- a/ASTree.cpp +++ b/ASTree.cpp @@ -123,9 +123,11 @@ PycRef BuildFromCode(PycRef code, PycModule* mod) && opcode != Pyc::JUMP_IF_FALSE_A && opcode != Pyc::JUMP_IF_FALSE_OR_POP_A && opcode != Pyc::POP_JUMP_IF_FALSE_A + && opcode != Pyc::POP_JUMP_FORWARD_IF_FALSE_A && opcode != Pyc::JUMP_IF_TRUE_A && opcode != Pyc::JUMP_IF_TRUE_OR_POP_A && opcode != Pyc::POP_JUMP_IF_TRUE_A + && opcode != Pyc::POP_JUMP_FORWARD_IF_TRUE_A && opcode != Pyc::POP_BLOCK) { else_pop = false; @@ -401,6 +403,19 @@ PycRef BuildFromCode(PycRef code, PycModule* mod) stack.push(new ASTTuple(values)); } break; + case Pyc::KW_NAMES_A: + { + + int kwparams = code->getConst(operand).cast()->size(); + ASTKwNamesMap kwparamList; + std::vector> keys = code->getConst(operand).cast()->values(); + for (int i = 0; i < kwparams; i++) { + kwparamList.add(new ASTObject(keys[kwparams - i - 1]), stack.top()); + stack.pop(); + } + stack.push(new ASTKwNamesMap(kwparamList)); + } + break; case Pyc::CALL_A: case Pyc::CALL_FUNCTION_A: { @@ -445,12 +460,31 @@ PycRef BuildFromCode(PycRef code, PycModule* mod) stack_hist.pop(); } - for (int i=0; i val = stack.top(); - stack.pop(); - PycRef key = stack.top(); - stack.pop(); - kwparamList.push_front(std::make_pair(key, val)); + /* + KW_NAMES(i) + Stores a reference to co_consts[consti] into an internal variable for use by CALL. + co_consts[consti] must be a tuple of strings. + New in version 3.11. + */ + if (mod->verCompare(3, 11) >= 0) { + PycRef object_or_map = stack.top(); + if (object_or_map.type() == ASTNode::NODE_KW_NAMES_MAP) { + stack.pop(); + PycRef kwparams_map = object_or_map.cast(); + for (ASTKwNamesMap::map_t::const_iterator it = kwparams_map->values().begin(); it != kwparams_map->values().end(); it++) { + kwparamList.push_front(std::make_pair(it->first, it->second)); + pparams -= 1; + } + } + } + else { + for (int i = 0; i < kwparams; i++) { + PycRef val = stack.top(); + stack.pop(); + PycRef key = stack.top(); + stack.pop(); + kwparamList.push_front(std::make_pair(key, val)); + } } for (int i=0; i param = stack.top(); @@ -1004,13 +1038,17 @@ PycRef BuildFromCode(PycRef code, PycModule* mod) case Pyc::JUMP_IF_TRUE_OR_POP_A: case Pyc::POP_JUMP_IF_FALSE_A: case Pyc::POP_JUMP_IF_TRUE_A: + case Pyc::POP_JUMP_FORWARD_IF_FALSE_A: + case Pyc::POP_JUMP_FORWARD_IF_TRUE_A: { PycRef cond = stack.top(); PycRef ifblk; int popped = ASTCondBlock::UNINITED; if (opcode == Pyc::POP_JUMP_IF_FALSE_A - || opcode == Pyc::POP_JUMP_IF_TRUE_A) { + || opcode == Pyc::POP_JUMP_IF_TRUE_A + || opcode == Pyc::POP_JUMP_FORWARD_IF_FALSE_A + || opcode == Pyc::POP_JUMP_FORWARD_IF_TRUE_A) { /* Pop condition before the jump */ stack.pop(); popped = ASTCondBlock::PRE_POPPED; @@ -1029,15 +1067,18 @@ PycRef BuildFromCode(PycRef code, PycModule* mod) /* "Jump if true" means "Jump if not false" */ bool neg = opcode == Pyc::JUMP_IF_TRUE_A || opcode == Pyc::JUMP_IF_TRUE_OR_POP_A - || opcode == Pyc::POP_JUMP_IF_TRUE_A; + || opcode == Pyc::POP_JUMP_IF_TRUE_A + || opcode == Pyc::POP_JUMP_FORWARD_IF_TRUE_A; int offs = operand; if (mod->verCompare(3, 10) >= 0) offs *= sizeof(uint16_t); // // BPO-27129 if (opcode == Pyc::JUMP_IF_FALSE_A - || opcode == Pyc::JUMP_IF_TRUE_A) { + || opcode == Pyc::JUMP_IF_TRUE_A + || opcode == Pyc::POP_JUMP_FORWARD_IF_TRUE_A + || opcode == Pyc::POP_JUMP_FORWARD_IF_FALSE_A) { /* Offset is relative in these cases */ - offs = pos + operand; + offs += pos; } if (cond.type() == ASTNode::NODE_COMPARE @@ -1455,6 +1496,21 @@ PycRef BuildFromCode(PycRef code, PycModule* mod) stack.push(new ASTName(code->getLocal(operand))); break; case Pyc::LOAD_GLOBAL_A: + if (mod->verCompare(3, 11) >= 0) { + if (operand & 1) { + /* Changed in version 3.11: + If the low bit of "NAMEI" (operand) is set, + then a NULL is pushed to the stack before the global variable. */ + stack.push(nullptr); + /* + and thats because for some reason for example 3 global functions: input, int, print. + it tries to load: 1, 3, 5 + all though we have only 3 names, so thats should be: (1-1)/2 = 0, (3-1)/2 = 1, (5-1)/2 = 2 + i dont know why, maybe because of the null push, but thats a FIX for now. + */ + operand = (int)((operand - 1) / 2); + } + } stack.push(new ASTName(code->getName(operand))); break; case Pyc::LOAD_LOCALS: diff --git a/bytecode.cpp b/bytecode.cpp index cad4ab0..229648e 100644 --- a/bytecode.cpp +++ b/bytecode.cpp @@ -152,7 +152,8 @@ bool Pyc::IsJumpOffsetArg(int opcode) return (opcode == Pyc::JUMP_FORWARD_A) || (opcode == Pyc::JUMP_IF_FALSE_A) || (opcode == Pyc::JUMP_IF_TRUE_A) || (opcode == Pyc::SETUP_LOOP_A) || (opcode == Pyc::SETUP_FINALLY_A) || (opcode == Pyc::SETUP_EXCEPT_A) || - (opcode == Pyc::FOR_LOOP_A) || (opcode == Pyc::FOR_ITER_A); + (opcode == Pyc::FOR_LOOP_A) || (opcode == Pyc::FOR_ITER_A) || + (opcode == Pyc::POP_JUMP_FORWARD_IF_FALSE_A) || (opcode == Pyc::POP_JUMP_FORWARD_IF_TRUE_A); } bool Pyc::IsCompareArg(int opcode) diff --git a/tests/compiled/test_kwnames.3.11.pyc b/tests/compiled/test_kwnames.3.11.pyc new file mode 100644 index 0000000..59d70bb Binary files /dev/null and b/tests/compiled/test_kwnames.3.11.pyc differ diff --git a/tests/compiled/test_pop_jump_forward_if_false.3.11.pyc b/tests/compiled/test_pop_jump_forward_if_false.3.11.pyc new file mode 100644 index 0000000..38b534a Binary files /dev/null and b/tests/compiled/test_pop_jump_forward_if_false.3.11.pyc differ diff --git a/tests/compiled/test_pop_jump_forward_if_true.3.11.pyc b/tests/compiled/test_pop_jump_forward_if_true.3.11.pyc new file mode 100644 index 0000000..dd81dfd Binary files /dev/null and b/tests/compiled/test_pop_jump_forward_if_true.3.11.pyc differ diff --git a/tests/input/test_kwnames.py b/tests/input/test_kwnames.py new file mode 100644 index 0000000..937cab5 --- /dev/null +++ b/tests/input/test_kwnames.py @@ -0,0 +1,3 @@ +def foo(x): + print(x) +foo(x=1) diff --git a/tests/input/test_pop_jump_forward_if_false.py b/tests/input/test_pop_jump_forward_if_false.py new file mode 100644 index 0000000..d54797d --- /dev/null +++ b/tests/input/test_pop_jump_forward_if_false.py @@ -0,0 +1,3 @@ +if 1 == 0: + print('true') +print('false (so jumping forward)') diff --git a/tests/input/test_pop_jump_forward_if_true.py b/tests/input/test_pop_jump_forward_if_true.py new file mode 100644 index 0000000..6afb416 --- /dev/null +++ b/tests/input/test_pop_jump_forward_if_true.py @@ -0,0 +1,3 @@ +if not 1 == 0: + print('not true') +print('true (so jumping forward)') diff --git a/tests/tokenized/test_kwnames.txt b/tests/tokenized/test_kwnames.txt new file mode 100644 index 0000000..c6b1913 --- /dev/null +++ b/tests/tokenized/test_kwnames.txt @@ -0,0 +1,5 @@ +def foo ( x ) : + +print ( x ) + +foo ( x = 1 ) diff --git a/tests/tokenized/test_pop_jump_forward_if_false.txt b/tests/tokenized/test_pop_jump_forward_if_false.txt new file mode 100644 index 0000000..02ce800 --- /dev/null +++ b/tests/tokenized/test_pop_jump_forward_if_false.txt @@ -0,0 +1,5 @@ +if 1 == 0 : + +print ( 'true' ) + +print ( 'false (so jumping forward)' ) diff --git a/tests/tokenized/test_pop_jump_forward_if_true.txt b/tests/tokenized/test_pop_jump_forward_if_true.txt new file mode 100644 index 0000000..6de06be --- /dev/null +++ b/tests/tokenized/test_pop_jump_forward_if_true.txt @@ -0,0 +1,5 @@ +if not 1 == 0 : + +print ( 'not true' ) + +print ( 'true (so jumping forward)' )