Files
openide/python/helpers/pydev/pydev_tests_python/test_bytecode_modification.py
Egor Eliseev 838a1d047b PY-39432 Fix bytecode modification
Add handling for the appearance of the command "EXTENDED_ARG".
Add overflow checking.

IJ-CR-27465

GitOrigin-RevId: ec665bf61f379f26edf384501b3a111db90be915
2022-09-19 08:30:46 +00:00

894 lines
36 KiB
Python

import dis
import sys
import unittest
from io import StringIO
from types import CodeType
import pytest
from _pydevd_frame_eval.pydevd_modify_bytecode import insert_code, \
add_jump_instruction, IS_PY310_OR_GREATER
from opcode import EXTENDED_ARG
TRACE_MESSAGE = "Trace called"
def tracing():
print(TRACE_MESSAGE)
def call_tracing():
return tracing()
def bar(a, b):
return a + b
IS_PY36_OR_GREATER = sys.version_info > (3, 6)
IS_PY37_OR_GREATER = sys.version_info > (3, 7)
LOAD_OPCODES = [dis.opmap[x] for x in ('LOAD_ATTR', 'LOAD_CONST', 'LOAD_FAST', 'LOAD_GLOBAL', 'LOAD_NAME')]
if IS_PY37_OR_GREATER:
LOAD_OPCODES.append(dis.opmap['LOAD_METHOD'])
@pytest.mark.skipif(not IS_PY36_OR_GREATER, reason='Test requires Python 3.6 or greater')
class TestInsertCode(unittest.TestCase):
lines_separator = "---Line tested---"
def check_insert_every_line(self, func_to_modify, func_to_insert, number_of_lines):
first_line = func_to_modify.__code__.co_firstlineno + 1
last_line = first_line + number_of_lines
for i in range(first_line, last_line):
self.check_insert_to_line_with_exec(func_to_modify, func_to_insert, i)
print(self.lines_separator)
def check_insert_to_line_with_exec(self, func_to_modify, func_to_insert, line_number):
code_orig = func_to_modify.__code__
code_to_insert = func_to_insert.__code__
success, result = insert_code(code_orig, code_to_insert, line_number)
exec(result)
output = sys.stdout.getvalue().strip().split(self.lines_separator)[-1]
self.assertTrue(TRACE_MESSAGE in output)
def check_insert_to_line_by_symbols(self, func_to_modify, func_to_insert, line_number, code_for_check):
code_orig = func_to_modify.__code__
code_to_insert = func_to_insert.__code__
success, result = insert_code(code_orig, code_to_insert, line_number)
self.compare_bytes_sequence(result, code_for_check, len(code_to_insert.co_code))
def compare_bytes_sequence(self, code1, code2, inserted_code_size):
"""
Compare code after modification and the real code
Since we add POP_JUMP_IF_TRUE instruction, we can't compare modified code and the real code. That's why we
allow some inaccuracies while code comparison
:param code1: result code after modification
:param code2: a real code for checking
:param inserted_code_size: size of inserted code
"""
seq1 = [(offset, op, arg) for offset, op, arg in dis._unpack_opargs(list(code1.co_code))]
seq2 = [(offset, op, arg) for offset, op, arg in dis._unpack_opargs(list(code2.co_code))]
assert len(seq1) == len(seq2), "Bytes sequences have different lengths %s != %s" % (len(seq1), len(seq2))
for i in range(len(seq1)):
of, op1, arg1 = seq1[i]
_, op2, arg2 = seq2[i]
if op1 != op2:
if op1 == 115 and op2 == 1:
# it's ok, because we added POP_JUMP_IF_TRUE manually, but it's POP_TOP in the real code
# inserted code - 2 (removed return instruction) - real code inserted
# Jump should be done to the beginning of inserted fragment
if IS_PY310_OR_GREATER:
self.assertEqual(arg1, (of - (inserted_code_size - 2)) // 2)
else:
self.assertEqual(arg1, of - (inserted_code_size - 2))
continue
elif op1 == EXTENDED_ARG and op2 == 12:
# we added a real UNARY_NOT to balance EXTENDED_ARG added by new jump instruction
# i.e. inserted code size was increased as well
inserted_code_size += 2
continue
self.assertEqual(op1, op2, "Different operators at offset {}".format(of))
if op1 in LOAD_OPCODES:
# When comparing arguments of the load operations we shouldn't rely only on arguments themselves,
# because their order may change. It's better to compare the actual values instead.
self.compare_load_args(of, code1, code2, op1, arg1, arg2)
elif arg1 != arg2:
self.assertEquals(arg1, arg2, "Different arguments at offset {}".format(of))
def compare_load_args(self, offset, code1, code2, opcode, arg1, arg2):
err_msg = "Different arguments at offset {}".format(offset)
if opcode == dis.opmap['LOAD_ATTR']:
assert code1.co_names[arg1] == code2.co_names[arg2], err_msg
elif opcode == dis.opmap['LOAD_CONST']:
const1, const2 = code1.co_consts[arg1], code2.co_consts[arg2]
if isinstance(const1, CodeType) and isinstance(const2, CodeType):
if const1.co_filename != const2.co_filename:
self.fail(err_msg)
elif opcode == dis.opmap['LOAD_FAST']:
assert code1.co_varnames[arg1] == code2.co_varnames[arg2], err_msg
elif opcode == dis.opmap['LOAD_GLOBAL']:
assert code1.co_names[arg1] == code2.co_names[arg2], err_msg
elif opcode == dis.opmap['LOAD_NAME']:
assert code1.co_names[arg1] == code2.co_names[arg2], err_msg
def test_line_before_py310(self):
def foo():
global global_loaded
global_loaded()
def method():
a = 10
b = 20
c = 20
success, result = insert_code(method.__code__, foo.__code__, method.__code__.co_firstlineno + 1)
assert success
assert list(result.co_lnotab) == [10, 1, 4, 1, 4, 1]
success, result = insert_code(method.__code__, foo.__code__, method.__code__.co_firstlineno + 2)
assert success
assert list(result.co_lnotab) == [0, 1, 14, 1, 4, 1]
success, result = insert_code(method.__code__, foo.__code__, method.__code__.co_firstlineno + 3)
assert success
assert list(result.co_lnotab) == [0, 1, 4, 1, 14, 1]
def test_assignment(self):
self.original_stdout = sys.stdout
sys.stdout = StringIO()
try:
def original():
a = 1
b = 2
c = 3
self.check_insert_every_line(original, tracing, 3)
finally:
sys.stdout = self.original_stdout
def test_for_loop(self):
self.original_stdout = sys.stdout
sys.stdout = StringIO()
try:
def original():
n = 3
sum = 0
for i in range(n):
sum += i
return sum
self.check_insert_every_line(original, tracing, 5)
finally:
sys.stdout = self.original_stdout
def test_if(self):
self.original_stdout = sys.stdout
sys.stdout = StringIO()
try:
def original():
if True:
a = 1
else:
a = 0
print(a)
self.check_insert_to_line_with_exec(original, tracing, original.__code__.co_firstlineno + 2)
self.check_insert_to_line_with_exec(original, tracing, original.__code__.co_firstlineno + 5)
finally:
sys.stdout = self.original_stdout
def test_else(self):
self.original_stdout = sys.stdout
sys.stdout = StringIO()
try:
def original():
if False:
a = 1
else:
a = 0
print(a)
self.check_insert_to_line_with_exec(original, tracing, original.__code__.co_firstlineno + 4)
self.check_insert_to_line_with_exec(original, tracing, original.__code__.co_firstlineno + 5)
finally:
sys.stdout = self.original_stdout
def test_for_else(self):
self.original_stdout = sys.stdout
sys.stdout = StringIO()
try:
def original():
sum = 0
for i in range(3):
sum += i
else:
print(sum)
def check_line_1():
tracing()
sum = 0
for i in range(3):
sum += i
else:
print(sum)
def check_line_3():
sum = 0
for i in range(3):
tracing()
sum += i
else:
print(sum)
def check_line_5():
sum = 0
for i in range(3):
sum += i
else:
tracing()
print(sum)
self.check_insert_to_line_with_exec(original, tracing, original.__code__.co_firstlineno + 1)
self.check_insert_to_line_with_exec(original, tracing, original.__code__.co_firstlineno + 3)
self.check_insert_to_line_with_exec(original, tracing, original.__code__.co_firstlineno + 5)
sys.stdout = self.original_stdout
self.check_insert_to_line_by_symbols(original, call_tracing, original.__code__.co_firstlineno + 1,
check_line_1.__code__)
self.check_insert_to_line_by_symbols(original, call_tracing, original.__code__.co_firstlineno + 3,
check_line_3.__code__)
self.check_insert_to_line_by_symbols(original, call_tracing, original.__code__.co_firstlineno + 5,
check_line_5.__code__)
finally:
sys.stdout = self.original_stdout
def test_elif(self):
self.original_stdout = sys.stdout
sys.stdout = StringIO()
try:
def original():
a = 5
b = 0
if a < 0:
print("a < 0")
elif a < 3:
print("a < 3")
else:
print("a >= 3")
b = a
return b
def check_line_1():
tracing()
a = 5
b = 0
if a < 0:
print("a < 0")
elif a < 3:
print("a < 3")
else:
print("a >= 3")
b = a
return b
def check_line_8():
a = 5
b = 0
if a < 0:
print("a < 0")
elif a < 3:
print("a < 3")
else:
tracing()
print("a >= 3")
b = a
return b
def check_line_9():
a = 5
b = 0
if a < 0:
print("a < 0")
elif a < 3:
print("a < 3")
else:
print("a >= 3")
tracing()
b = a
return b
self.check_insert_to_line_with_exec(original, tracing, original.__code__.co_firstlineno + 1)
self.check_insert_to_line_with_exec(original, tracing, original.__code__.co_firstlineno + 2)
self.check_insert_to_line_with_exec(original, tracing, original.__code__.co_firstlineno + 8)
self.check_insert_to_line_with_exec(original, tracing, original.__code__.co_firstlineno + 9)
self.check_insert_to_line_by_symbols(original, call_tracing, original.__code__.co_firstlineno + 1,
check_line_1.__code__)
self.check_insert_to_line_by_symbols(original, call_tracing, original.__code__.co_firstlineno + 8,
check_line_8.__code__)
self.check_insert_to_line_by_symbols(original, call_tracing, original.__code__.co_firstlineno + 9,
check_line_9.__code__)
finally:
sys.stdout = self.original_stdout
def test_call_other_function(self):
self.original_stdout = sys.stdout
sys.stdout = StringIO()
try:
def original():
a = 1
b = 3
c = bar(a, b)
return c
def check_line_3():
a = 1
b = 3
tracing()
c = bar(a, b)
return c
def check_line_4():
a = 1
b = 3
c = bar(a, b)
tracing()
return c
self.check_insert_every_line(original, tracing, 4)
sys.stdout = self.original_stdout
self.check_insert_to_line_by_symbols(original, call_tracing, original.__code__.co_firstlineno + 3,
check_line_3.__code__)
self.check_insert_to_line_by_symbols(original, call_tracing, original.__code__.co_firstlineno + 4,
check_line_4.__code__)
finally:
sys.stdout = self.original_stdout
def test_class_method(self):
self.original_stdout = sys.stdout
sys.stdout = StringIO()
try:
class A(object):
@staticmethod
def foo():
print("i'm in foo")
@staticmethod
def check_line_2():
tracing()
print("i'm in foo")
original = A.foo
self.check_insert_to_line_with_exec(original, tracing, original.__code__.co_firstlineno + 2)
self.check_insert_to_line_by_symbols(original, call_tracing, original.__code__.co_firstlineno + 2,
A.check_line_2.__code__)
finally:
sys.stdout = self.original_stdout
def test_offset_overflow(self):
self.original_stdout = sys.stdout
sys.stdout = StringIO()
try:
def foo():
a = 1
b = 2 # breakpoint
c = 3
a1 = 1 if a > 1 else 2
a2 = 1 if a > 1 else 2
a3 = 1 if a > 1 else 2
a4 = 1 if a > 1 else 2
a5 = 1 if a > 1 else 2
a6 = 1 if a > 1 else 2
a7 = 1 if a > 1 else 2
a8 = 1 if a > 1 else 2
a9 = 1 if a > 1 else 2
a10 = 1 if a > 1 else 2
a11 = 1 if a > 1 else 2
a12 = 1 if a > 1 else 2
a13 = 1 if a > 1 else 2
for i in range(1):
if a > 0:
print("111")
# a = 1
else:
print("222")
return b
def check_line_2():
a = 1
tracing()
b = 2
c = 3
a1 = 1 if a > 1 else 2
a2 = 1 if a > 1 else 2
a3 = 1 if a > 1 else 2
a4 = 1 if a > 1 else 2
a5 = 1 if a > 1 else 2
a6 = 1 if a > 1 else 2
a7 = 1 if a > 1 else 2
a8 = 1 if a > 1 else 2
a9 = 1 if a > 1 else 2
a10 = 1 if a > 1 else 2
a11 = 1 if a > 1 else 2
a12 = 1 if a > 1 else 2
a13 = 1 if a > 1 else 2
for i in range(1):
if a > 0:
print("111")
# a = 1
else:
print("222")
return b
self.check_insert_to_line_with_exec(foo, tracing, foo.__code__.co_firstlineno + 2)
self.check_insert_to_line_by_symbols(foo, call_tracing, foo.__code__.co_firstlineno + 2,
check_line_2.__code__)
finally:
sys.stdout = self.original_stdout
def test_long_lines(self):
self.original_stdout = sys.stdout
sys.stdout = StringIO()
try:
def foo():
a = 1
b = 1 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23
c = 1 if b > 1 else 2 if b > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23
d = 1 if c > 1 else 2 if c > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23
e = d + 1
return e
def check_line_2():
a = 1
tracing()
b = 1 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23
c = 1 if b > 1 else 2 if b > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23
d = 1 if c > 1 else 2 if c > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23
e = d + 1
return e
self.check_insert_to_line_with_exec(foo, tracing, foo.__code__.co_firstlineno + 2)
sys.stdout = self.original_stdout
self.check_insert_to_line_by_symbols(foo, call_tracing, foo.__code__.co_firstlineno + 2,
check_line_2.__code__)
finally:
sys.stdout = self.original_stdout
def test_many_names(self):
self.original_stdout = sys.stdout
sys.stdout = StringIO()
try:
from pydev_tests_python.resources._bytecode_many_names_example import foo
self.check_insert_to_line_with_exec(foo, tracing, foo.__code__.co_firstlineno + 2)
finally:
sys.stdout = self.original_stdout
def test_extended_arg_overflow(self):
from pydev_tests_python.resources._bytecode_overflow_example import Dummy, DummyTracing
self.check_insert_to_line_by_symbols(Dummy.fun, call_tracing, Dummy.fun.__code__.co_firstlineno + 3,
DummyTracing.fun.__code__)
@pytest.mark.skipif(
IS_PY310_OR_GREATER, reason="Test is specific for Python versions < 3.10")
def test_double_extended_arg_before_py310(self):
self.original_stdout = sys.stdout
sys.stdout = StringIO()
try:
def foo():
a = 1
b = 2
if b > 0:
d = a + b
d += 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
a = a + 1
return a
def foo_check_3():
a = 1
b = 2
tracing()
if b > 0:
d = a + b
d += 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
a = a + 1
return a
def foo_check_21():
a = 1
b = 2
if b > 0:
d = a + b
d += 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
not tracing() # add 'not' to balance EXTENDED_ARG when jumping
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
a = a + 1
return a
self.check_insert_to_line_with_exec(foo, tracing, foo.__code__.co_firstlineno + 2)
sys.stdout = self.original_stdout
self.check_insert_to_line_by_symbols(foo, call_tracing, foo.__code__.co_firstlineno + 3,
foo_check_3.__code__)
self.check_insert_to_line_by_symbols(foo, call_tracing, foo.__code__.co_firstlineno + 21,
foo_check_21.__code__)
finally:
sys.stdout = self.original_stdout
@pytest.mark.skipif(
not IS_PY310_OR_GREATER, reason="Test is specific for Python versions >= 3.10")
def test_double_extended_arg_after_py310(self):
self.original_stdout = sys.stdout
sys.stdout = StringIO()
try:
def foo():
a = 1
b = 2
if b > 0:
d = a + b
d += 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
a = a + 1
return a
def foo_check_3():
a = 1
b = 2
tracing()
if b > 0:
d = a + b
d += 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
a = a + 1
return a
def foo_check_40():
a = 1
b = 2
if b > 0:
d = a + b
d += 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
not tracing() # add 'not' to balance EXTENDED_ARG when jumping
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
b = b - 1 if a > 0 else b + 1
a = a + 1
return a
self.check_insert_to_line_with_exec(foo, tracing, foo.__code__.co_firstlineno + 2)
sys.stdout = self.original_stdout
self.check_insert_to_line_by_symbols(foo, call_tracing, foo.__code__.co_firstlineno + 3,
foo_check_3.__code__)
self.check_insert_to_line_by_symbols(foo, call_tracing, foo.__code__.co_firstlineno + 40,
foo_check_40.__code__)
finally:
sys.stdout = self.original_stdout
def test_add_jump_instruction(self):
def foo():
a = 1
b = 2
return a + b
offset = 0
if IS_PY310_OR_GREATER:
offset *= 2
new_code_list = add_jump_instruction(offset, foo.__code__)
assert new_code_list[-1] == 0
assert new_code_list[-2] == dis.opmap['POP_JUMP_IF_TRUE']
offset = 42
if IS_PY310_OR_GREATER:
offset *= 2
new_code_list = add_jump_instruction(offset, foo.__code__)
assert new_code_list[-1] == 42
assert new_code_list[-2] == dis.opmap['POP_JUMP_IF_TRUE']
offset = 257
if IS_PY310_OR_GREATER:
offset *= 2
new_code_list = add_jump_instruction(offset, foo.__code__)
assert new_code_list[-1] == 1
assert new_code_list[-2] == dis.opmap['POP_JUMP_IF_TRUE']
assert new_code_list[-3] == 1
assert new_code_list[-4] == EXTENDED_ARG
offset = 65602
if IS_PY310_OR_GREATER:
offset *= 2
new_code_list = add_jump_instruction(offset, foo.__code__)
assert new_code_list[-1] == 66
assert new_code_list[-2] == dis.opmap['POP_JUMP_IF_TRUE']
assert new_code_list[-3] == 0
assert new_code_list[-4] == EXTENDED_ARG
assert new_code_list[-5] == 1
assert new_code_list[-6] == EXTENDED_ARG
offset = 4294967296 # Doesn't fit into 4 EXTENDED_ARGs.
if IS_PY310_OR_GREATER:
offset *= 2
with(pytest.raises(RuntimeError)):
add_jump_instruction(offset, foo.__code__)
@pytest.mark.skipif(
IS_PY310_OR_GREATER, reason="Test is specific for Python versions < 3.10")
def testing_add_extended_arg(self):
def backtrack(move_list, board_p, number_p):
for i in range(2, len(move_list)):
x = move_list[-i][0]
y = move_list[-i][1]
available_0 = False
try:
if board_p[y + 1][x] == 0:
available_0 = True
new_x = x
new_y = y + 1
except IndexError:
available_0 = False
if not available_0:
if y - 1 != -1:
if board_p[y - 1][x] == 0:
available_0 = True
new_x = x
new_y = y - 1
else:
available_0 = False
if not available_0: # first error
try:
if board_p[y][x + 1] == 0:
available_0 = True
new_x = x + 1
new_y = y
except IndexError: # second error
available_0 = False
if not available_0:
if x - 1 != -1:
if board_p[y][x - 1] == 0:
available_0 = True
new_x = x - 1
new_y = y
else:
available_0 = False
if available_0:
# set all moves after chosen one back to 0 in board - tick
# return new board - tick
# pop all useless moves - tick
# return new move list - tick
# have to send back new cell number based on how many deleted
index = move_list.index((x, y))
counter = 0
for g in range(len(move_list) - 1, index, -1):
board_p[move_list[g][1]][move_list[g][0]] = 0
del move_list[g]
counter += 1
number_p -= counter - 1
return new_x, new_y, board_p, move_list
line = backtrack.__code__.co_firstlineno + 1
from _pydevd_frame_eval.pydevd_frame_tracing import create_pydev_trace_code_wrapper
breakpoint_code = create_pydev_trace_code_wrapper(line)
success, result = insert_code(backtrack.__code__, breakpoint_code, line)
assert success
result_lnotab = list(result.co_lnotab)
# first error, see `backtrack` function
assert result_lnotab[19 * 2] == 6
assert result_lnotab[19 * 2 + 1] == 1
# second error, see `backtrack` function
assert result_lnotab[25 * 2] == 16
assert result_lnotab[25 * 2 + 1] == 1