mirror of
https://github.com/microsoft/debugpy.git
synced 2025-12-23 08:48:12 +00:00
Python 3.10 support (work in progress).
Updated bytecode library (and other minors).
This commit is contained in:
parent
e59195d6a4
commit
3207466fc3
44 changed files with 559 additions and 454 deletions
|
|
@ -45,7 +45,7 @@
|
|||
"git": {
|
||||
"Name": "bytecode",
|
||||
"RepositoryUrl": "https://github.com/MatthieuDartiailh/bytecode",
|
||||
"CommitHash": "826344ec533643fad068754e0b9799bfeac46634"
|
||||
"CommitHash": "ccb8a7b4bf12a85ff53c3c42517d2633c5225829"
|
||||
},
|
||||
"DevelopmentDependency": false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1032,7 +1032,7 @@ class _NewThreadStartupWithTrace:
|
|||
# Note: if this is a thread from threading.py, we're too early in the boostrap process (because we mocked
|
||||
# the start_new_thread internal machinery and thread._bootstrap has not finished), so, the code below needs
|
||||
# to make sure that we use the current thread bound to the original function and not use
|
||||
# current_thread() unless we're sure it's a dummy thread.
|
||||
# threading.current_thread() unless we're sure it's a dummy thread.
|
||||
t = getattr(self.original_func, '__self__', getattr(self.original_func, 'im_self', None))
|
||||
if not isinstance(t, threading.Thread):
|
||||
# This is not a threading.Thread but a Dummy thread (so, get it as a dummy thread using
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ class CommunicationThread(threading.Thread):
|
|||
|
||||
def __init__(self, tests_queue):
|
||||
threading.Thread.__init__(self)
|
||||
self.setDaemon(True)
|
||||
self.daemon = True
|
||||
self.queue = tests_queue
|
||||
self.finished = False
|
||||
from _pydev_bundle.pydev_imports import SimpleXMLRPCServer
|
||||
|
|
@ -243,7 +243,7 @@ class ClientThread(threading.Thread):
|
|||
|
||||
def __init__(self, job_id, port, verbosity, coverage_output_file=None, coverage_include=None):
|
||||
threading.Thread.__init__(self)
|
||||
self.setDaemon(True)
|
||||
self.daemon = True
|
||||
self.port = port
|
||||
self.job_id = job_id
|
||||
self.verbosity = verbosity
|
||||
|
|
|
|||
|
|
@ -194,6 +194,11 @@ class _StackInterpreter(object):
|
|||
on_DICT_UPDATE = on_POP_TOP
|
||||
on_SET_UPDATE = on_POP_TOP
|
||||
|
||||
on_GEN_START = on_POP_TOP
|
||||
|
||||
def on_NOP(self, instr):
|
||||
pass
|
||||
|
||||
def _handle_call_from_instr(self, func_name_instr, func_call_instr):
|
||||
self.load_attrs.pop(_TargetIdHashable(func_name_instr), None)
|
||||
call_name = self._getcallname(func_name_instr)
|
||||
|
|
|
|||
|
|
@ -184,6 +184,7 @@ IS_PY36_OR_GREATER = False
|
|||
IS_PY37_OR_GREATER = False
|
||||
IS_PY38_OR_GREATER = False
|
||||
IS_PY39_OR_GREATER = False
|
||||
IS_PY310_OR_GREATER = False
|
||||
IS_PY2 = True
|
||||
IS_PY27 = False
|
||||
IS_PY24 = False
|
||||
|
|
@ -197,6 +198,7 @@ try:
|
|||
IS_PY37_OR_GREATER = sys.version_info >= (3, 7)
|
||||
IS_PY38_OR_GREATER = sys.version_info >= (3, 8)
|
||||
IS_PY39_OR_GREATER = sys.version_info >= (3, 9)
|
||||
IS_PY310_OR_GREATER = sys.version_info >= (3, 10)
|
||||
elif sys.version_info[0] == 2 and sys.version_info[1] == 7:
|
||||
IS_PY27 = True
|
||||
elif sys.version_info[0] == 2 and sys.version_info[1] == 4:
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ def notify_error(*args):
|
|||
#=======================================================================================================================
|
||||
def code_objects_equal(code0, code1):
|
||||
for d in dir(code0):
|
||||
if d.startswith('_') or 'lineno' in d or d == 'replace':
|
||||
if d.startswith('_') or 'line' in d or d == 'replace':
|
||||
continue
|
||||
if getattr(code0, d) != getattr(code1, d):
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ def getVariable(dbg, thread_id, frame_id, scope, attrs):
|
|||
not the frame (as we don't care about the frame in this case).
|
||||
"""
|
||||
if scope == 'BY_ID':
|
||||
if thread_id != get_current_thread_id(current_thread()):
|
||||
if thread_id != get_current_thread_id(threading.current_thread()):
|
||||
raise VariableError("getVariable: must execute on same thread")
|
||||
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -8,9 +8,11 @@ pip install bytecode --target .
|
|||
|
||||
or from master (if needed for some early bugfix):
|
||||
|
||||
python -m pip install https://github.com/vstinner/bytecode/archive/master.zip --target .
|
||||
python -m pip install https://github.com/MatthieuDartiailh/bytecode/archive/main.zip --target .
|
||||
|
||||
Then run 'pydevd_fix_code.py' to fix the imports on the vendored file, run its tests (to see
|
||||
if things are still ok) and commit.
|
||||
|
||||
Then, to finish, apply the patch to add the offset to the instructions (bcb8a28669e9178f96f5d71af7259e0674acc47c)
|
||||
|
||||
Note: commit the egg-info as a note of the license (force if needed).
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
bytecode-0.12.0.dist-info/COPYING,sha256=nPUTV23AcD5UZcc9EtVTBt-Igoyj7IIRw97xuOGVgq8,1095
|
||||
bytecode-0.12.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
bytecode-0.12.0.dist-info/METADATA,sha256=asu9c0mCLVYLxmfElINQCm0lAMBdVcgZqMIoCI1Zfnk,2589
|
||||
bytecode-0.12.0.dist-info/RECORD,,
|
||||
bytecode-0.12.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
bytecode-0.12.0.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92
|
||||
bytecode-0.12.0.dist-info/top_level.txt,sha256=9BhdB7HqYZ-PvHNoWX6ilwLYWQqcgEOLwdb3aXm5Gys,9
|
||||
bytecode/__init__.py,sha256=8krUbp5Kcxe_oC3S5OXwB-VXoMP_V-13FV0eLLFPPs4,4011
|
||||
bytecode/__pycache__/__init__.cpython-38.pyc,,
|
||||
bytecode/__pycache__/bytecode.cpython-38.pyc,,
|
||||
bytecode/__pycache__/cfg.cpython-38.pyc,,
|
||||
bytecode/__pycache__/concrete.cpython-38.pyc,,
|
||||
bytecode/__pycache__/flags.cpython-38.pyc,,
|
||||
bytecode/__pycache__/instr.cpython-38.pyc,,
|
||||
bytecode/__pycache__/peephole_opt.cpython-38.pyc,,
|
||||
bytecode/bytecode.py,sha256=8eZ6vZTfCFEB0lwupJZsZds5u6Is7wlX6IlR8olG7QQ,6897
|
||||
bytecode/cfg.py,sha256=NGBJ5lZuCQuAHvyWiyUHPXGBA_8ze3CzyXvJxmcYTWk,15239
|
||||
bytecode/concrete.py,sha256=0Ojd-mED2COoB_fzEHL6OcW7zqtb76k6lEbkvJPNCQY,21281
|
||||
bytecode/flags.py,sha256=opG0Yh8oQ0EUPIAA5S3TIHHaojYO_sV3RHc3WSFlopg,5956
|
||||
bytecode/instr.py,sha256=Zavx4BNwJfWLNZUIGXukGPInxTsflyZeZ6cAcqq1ggk,11832
|
||||
bytecode/peephole_opt.py,sha256=_XI45yErqZ4pGEUqRQVcMl5vS2IY4dqcrCGmHIHJdow,16203
|
||||
bytecode/tests/__init__.py,sha256=tpG_2bYVAAFqVQz2ZbkZ-XQAX8k8rQjBbAUVhZqMLkA,5163
|
||||
bytecode/tests/__pycache__/__init__.cpython-38.pyc,,
|
||||
bytecode/tests/__pycache__/sanitytest.cpython-38.pyc,,
|
||||
bytecode/tests/__pycache__/test_bytecode.cpython-38.pyc,,
|
||||
bytecode/tests/__pycache__/test_cfg.cpython-38.pyc,,
|
||||
bytecode/tests/__pycache__/test_code.cpython-38.pyc,,
|
||||
bytecode/tests/__pycache__/test_concrete.cpython-38.pyc,,
|
||||
bytecode/tests/__pycache__/test_flags.cpython-38.pyc,,
|
||||
bytecode/tests/__pycache__/test_instr.cpython-38.pyc,,
|
||||
bytecode/tests/__pycache__/test_misc.cpython-38.pyc,,
|
||||
bytecode/tests/__pycache__/test_peephole_opt.cpython-38.pyc,,
|
||||
bytecode/tests/sanitytest.py,sha256=-UiwWgJe9y6LC2Bim0ZdV6aKN47b3uJ_RWELbfzO_0g,711
|
||||
bytecode/tests/test_bytecode.py,sha256=3dH08nPMs_T6sZXHybS0beA8pTvBeyUqLIS5WaVx1VQ,15150
|
||||
bytecode/tests/test_cfg.py,sha256=h-Z7tZsWDqdW0BEzQBYmnKKlw2LqeeiKGpiUwPc4ADI,29056
|
||||
bytecode/tests/test_code.py,sha256=8wqx8uli3_UXewDeh6wt52oDZcRp3L7SXYoGkmIP1Zg,1697
|
||||
bytecode/tests/test_concrete.py,sha256=uamFKnciMljHHIL7U691KQemFE0EazvhSOV1ErcgUF4,49375
|
||||
bytecode/tests/test_flags.py,sha256=ve1FXkaXO9mBly4EO0vTySeWZC4x17yyBMdfvHNtWqc,5838
|
||||
bytecode/tests/test_instr.py,sha256=ucFNTYxuHN1S2-lIuIVl9QV0zL4DDnUGmcbz5Je2ONo,11652
|
||||
bytecode/tests/test_misc.py,sha256=l5WOEITwedUz_PbZu3Z1E5qk4lSDpEAeIRNGKHONTJE,7704
|
||||
bytecode/tests/test_peephole_opt.py,sha256=kPEUtsgpCkM889c1zV0GcaB1Iy3F75ypJMQYBpHQZdI,33620
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
Metadata-Version: 2.1
|
||||
Name: bytecode
|
||||
Version: 0.12.0
|
||||
Version: 0.13.0.dev0
|
||||
Summary: Python module to generate and modify bytecode
|
||||
Home-page: https://github.com/MatthieuDartiailh/bytecode
|
||||
Author: Victor Stinner
|
||||
|
|
@ -15,8 +15,12 @@ Classifier: License :: OSI Approved :: MIT License
|
|||
Classifier: Natural Language :: English
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.6
|
||||
Classifier: Programming Language :: Python :: 3.7
|
||||
Classifier: Programming Language :: Python :: 3.8
|
||||
Classifier: Programming Language :: Python :: 3.9
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||
Requires-Dist: aenum (>=2.0) ; python_version < "3.6"
|
||||
Requires-Python: >=3.6
|
||||
|
||||
********
|
||||
bytecode
|
||||
|
|
@ -51,8 +55,10 @@ bytecode
|
|||
* `Download latest bytecode release at the Python Cheeseshop (PyPI)
|
||||
<https://pypi.python.org/pypi/bytecode>`_
|
||||
|
||||
Install bytecode: ``python3 -m pip install bytecode``. It requires Python 3.5
|
||||
or newer.
|
||||
Install bytecode: ``python3 -m pip install bytecode``. It requires Python 3.6
|
||||
or newer. The latest release that supports Python 3.5 is 0.12.0. For Python 2.7
|
||||
support, have a look at `dead-bytecode
|
||||
<https://github.com/p403n1x87/dead-bytecode>`_ instead.
|
||||
|
||||
Example executing ``print('Hello World!')``:
|
||||
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
bytecode-0.13.0.dev0.dist-info/COPYING,sha256=baWkm-Te2LLURwK7TL0zOkMSVjVCU_ezvObHBo298Tk,1074
|
||||
bytecode-0.13.0.dev0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
bytecode-0.13.0.dev0.dist-info/METADATA,sha256=9XadDK6YTQ-FPowYI5DS4ieA7hRGnRP_fM5Z9ioPkEQ,2929
|
||||
bytecode-0.13.0.dev0.dist-info/RECORD,,
|
||||
bytecode-0.13.0.dev0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
bytecode-0.13.0.dev0.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92
|
||||
bytecode-0.13.0.dev0.dist-info/direct_url.json,sha256=s58Rb4KXRlMKxk-mzpvr_tJRQ-Hx8-DHsU6NdohCnAg,93
|
||||
bytecode-0.13.0.dev0.dist-info/top_level.txt,sha256=9BhdB7HqYZ-PvHNoWX6ilwLYWQqcgEOLwdb3aXm5Gys,9
|
||||
bytecode/__init__.py,sha256=d-yk4Xh4SwOWq9NgoD2rmBLG6RhUFNljeqs-NjMNSYM,3885
|
||||
bytecode/__pycache__/__init__.cpython-38.pyc,,
|
||||
bytecode/__pycache__/bytecode.cpython-38.pyc,,
|
||||
bytecode/__pycache__/cfg.cpython-38.pyc,,
|
||||
bytecode/__pycache__/concrete.cpython-38.pyc,,
|
||||
bytecode/__pycache__/flags.cpython-38.pyc,,
|
||||
bytecode/__pycache__/instr.cpython-38.pyc,,
|
||||
bytecode/__pycache__/peephole_opt.cpython-38.pyc,,
|
||||
bytecode/bytecode.py,sha256=IMCcatHMtQ7M31nwj4r3drcvQuGVJAOP0d7C0O8P_SE,6894
|
||||
bytecode/cfg.py,sha256=RmJGJqwCxR-XYaPH9YGY4wNDycdtLvIBJb1OGSmxcN0,15274
|
||||
bytecode/concrete.py,sha256=0eb6Yh_NDLmzJNcMs2TFom0EqFVSM1cO3inMH90YE-s,21683
|
||||
bytecode/flags.py,sha256=hAvM_B2yQKRw44leHP0oCae0aaJraAbDDTpqIf4I1CM,5987
|
||||
bytecode/instr.py,sha256=HYc65LjNSOB3GCWkNkCSkee1rRzUyr89rgdjbKBaTpE,11616
|
||||
bytecode/peephole_opt.py,sha256=W-cFVPOZN-JKfDV3aImsYenDSZkSNBDTVQqeMrGPU18,15712
|
||||
bytecode/tests/__init__.py,sha256=BAdOXXNRdMVX4D8TuRYPlG9PHU7Cb0bzvyfA9s435kM,4968
|
||||
bytecode/tests/__pycache__/__init__.cpython-38.pyc,,
|
||||
bytecode/tests/__pycache__/test_bytecode.cpython-38.pyc,,
|
||||
bytecode/tests/__pycache__/test_cfg.cpython-38.pyc,,
|
||||
bytecode/tests/__pycache__/test_code.cpython-38.pyc,,
|
||||
bytecode/tests/__pycache__/test_concrete.cpython-38.pyc,,
|
||||
bytecode/tests/__pycache__/test_flags.cpython-38.pyc,,
|
||||
bytecode/tests/__pycache__/test_instr.cpython-38.pyc,,
|
||||
bytecode/tests/__pycache__/test_misc.cpython-38.pyc,,
|
||||
bytecode/tests/__pycache__/test_peephole_opt.cpython-38.pyc,,
|
||||
bytecode/tests/__pycache__/util_annotation.cpython-38.pyc,,
|
||||
bytecode/tests/test_bytecode.py,sha256=buvtlDC0NwoQ3zuZ7OENIIDngSqtiO9WkAa2-UvxGkI,15584
|
||||
bytecode/tests/test_cfg.py,sha256=c0xT8OfV-mDHu-DIDWr6LVlZQyK4GfgLSmT5AsodbMk,28194
|
||||
bytecode/tests/test_code.py,sha256=XCOH29rOXSoQz130s-AIC62r23e9qNjk8Y2xDB2LmSc,2100
|
||||
bytecode/tests/test_concrete.py,sha256=qT2qvabkF0yC7inniNx53cMSDN-2Qi0IE3pwBZSzF8g,49253
|
||||
bytecode/tests/test_flags.py,sha256=DY9U3c6tJdxJFm0jEm_To1Cc0I99EidQv_0guud-4oE,5684
|
||||
bytecode/tests/test_instr.py,sha256=rYeF8u-L0aW8bLPBxTUSy_T7KP6SaXyJKv9OhC8k6aA,11295
|
||||
bytecode/tests/test_misc.py,sha256=wyK1wpVPHRfaXgo-EqUI-F1nyB9-UACerHsHbExAo1U,6758
|
||||
bytecode/tests/test_peephole_opt.py,sha256=niUfhgEbiFR7IAmdQ_N9Qgh7D3wdRQ_zS0V8mKC4EzI,32640
|
||||
bytecode/tests/util_annotation.py,sha256=wKq6yPWrzkNlholl5Y10b3VjuCkoiYVgvcIjk_8jzf8,485
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"archive_info": {}, "url": "https://github.com/MatthieuDartiailh/bytecode/archive/main.zip"}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
__version__ = "0.12.0"
|
||||
__version__ = "0.13.0.dev"
|
||||
|
||||
__all__ = [
|
||||
"Label",
|
||||
|
|
|
|||
|
|
@ -198,8 +198,12 @@ class Bytecode(_InstrList, _BaseBytecodeList):
|
|||
def to_code(
|
||||
self, compute_jumps_passes=None, stacksize=None, *, check_pre_and_post=True
|
||||
):
|
||||
# Prevent reconverting the concrete bytecode to bytecode and cfg to do the
|
||||
# calculation if we need to do it.
|
||||
if stacksize is None:
|
||||
stacksize = self.compute_stacksize(check_pre_and_post=check_pre_and_post)
|
||||
bc = self.to_concrete_bytecode(compute_jumps_passes=compute_jumps_passes)
|
||||
return bc.to_code(stacksize=stacksize, check_pre_and_post=check_pre_and_post)
|
||||
return bc.to_code(stacksize=stacksize)
|
||||
|
||||
def to_concrete_bytecode(self, compute_jumps_passes=None):
|
||||
converter = _bytecode._ConvertBytecodeToConcrete(self)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import sys
|
||||
|
||||
# alias to keep the 'bytecode' variable free
|
||||
from _pydevd_frame_eval.vendored import bytecode as _bytecode
|
||||
from _pydevd_frame_eval.vendored.bytecode.concrete import ConcreteInstr
|
||||
from _pydevd_frame_eval.vendored.bytecode.flags import CompilerFlags
|
||||
from _pydevd_frame_eval.vendored.bytecode.instr import Label, SetLineno, Instr
|
||||
|
||||
|
||||
|
|
@ -219,8 +222,20 @@ class ControlFlowGraph(_bytecode.BaseBytecode):
|
|||
block.seen = False
|
||||
block.startsize = -32768 # INT_MIN
|
||||
|
||||
# Starting with Python 3.10, generator and coroutines start with one object
|
||||
# on the stack (None, anything is an error).
|
||||
initial_stack_size = 0
|
||||
if sys.version_info >= (3, 10) and self.flags & (
|
||||
CompilerFlags.GENERATOR
|
||||
| CompilerFlags.COROUTINE
|
||||
| CompilerFlags.ASYNC_GENERATOR
|
||||
):
|
||||
initial_stack_size = 1
|
||||
|
||||
# Create a generator/coroutine responsible of dealing with the first block
|
||||
coro = _compute_stack_size(self[0], 0, 0, check_pre_and_post=check_pre_and_post)
|
||||
coro = _compute_stack_size(
|
||||
self[0], initial_stack_size, 0, check_pre_and_post=check_pre_and_post
|
||||
)
|
||||
|
||||
# Create a list of generator that have not yet been exhausted
|
||||
coroutines = []
|
||||
|
|
|
|||
|
|
@ -19,7 +19,11 @@ from _pydevd_frame_eval.vendored.bytecode.instr import (
|
|||
_check_arg_int,
|
||||
)
|
||||
|
||||
_WORDCODE = sys.version_info >= (3, 6)
|
||||
|
||||
# - jumps use instruction
|
||||
# - lineno use bytes (dis.findlinestarts(code))
|
||||
# - dis displays bytes
|
||||
OFFSET_AS_INSTRUCTION = sys.version_info >= (3, 10)
|
||||
|
||||
|
||||
def _set_docstring(code, consts):
|
||||
|
|
@ -60,22 +64,13 @@ class ConcreteInstr(Instr):
|
|||
|
||||
def _set(self, name, arg, lineno):
|
||||
super()._set(name, arg, lineno)
|
||||
if _WORDCODE:
|
||||
size = 2
|
||||
if arg is not UNSET:
|
||||
while arg > 0xFF:
|
||||
size += 2
|
||||
arg >>= 8
|
||||
if self._extended_args is not None:
|
||||
size = 2 + 2 * self._extended_args
|
||||
else:
|
||||
size = 1
|
||||
if arg is not UNSET:
|
||||
size = 2
|
||||
if arg is not UNSET:
|
||||
while arg > 0xFF:
|
||||
size += 2
|
||||
if arg > 0xFFFF:
|
||||
size += 3
|
||||
if self._extended_args is not None:
|
||||
size = 1 + 3 * self._extended_args
|
||||
arg >>= 8
|
||||
if self._extended_args is not None:
|
||||
size = 2 + 2 * self._extended_args
|
||||
self._size = size
|
||||
|
||||
@property
|
||||
|
|
@ -87,64 +82,41 @@ class ConcreteInstr(Instr):
|
|||
|
||||
def get_jump_target(self, instr_offset):
|
||||
if self._opcode in _opcode.hasjrel:
|
||||
return instr_offset + self._size + self._arg
|
||||
s = (self._size // 2) if OFFSET_AS_INSTRUCTION else self._size
|
||||
return instr_offset + s + self._arg
|
||||
if self._opcode in _opcode.hasjabs:
|
||||
return self._arg
|
||||
return None
|
||||
|
||||
if _WORDCODE:
|
||||
def assemble(self):
|
||||
if self._arg is UNSET:
|
||||
return bytes((self._opcode, 0))
|
||||
|
||||
def assemble(self):
|
||||
if self._arg is UNSET:
|
||||
return bytes((self._opcode, 0))
|
||||
arg = self._arg
|
||||
b = [self._opcode, arg & 0xFF]
|
||||
while arg > 0xFF:
|
||||
arg >>= 8
|
||||
b[:0] = [_opcode.EXTENDED_ARG, arg & 0xFF]
|
||||
|
||||
arg = self._arg
|
||||
b = [self._opcode, arg & 0xFF]
|
||||
while arg > 0xFF:
|
||||
arg >>= 8
|
||||
b[:0] = [_opcode.EXTENDED_ARG, arg & 0xFF]
|
||||
if self._extended_args:
|
||||
while len(b) < self._size:
|
||||
b[:0] = [_opcode.EXTENDED_ARG, 0x00]
|
||||
|
||||
if self._extended_args:
|
||||
while len(b) < self._size:
|
||||
b[:0] = [_opcode.EXTENDED_ARG, 0x00]
|
||||
|
||||
return bytes(b)
|
||||
|
||||
else:
|
||||
|
||||
def assemble(self):
|
||||
if self._arg is UNSET:
|
||||
return struct.pack("<B", self._opcode)
|
||||
|
||||
arg = self._arg
|
||||
if arg > 0xFFFF:
|
||||
b = struct.pack(
|
||||
"<BHBH", _opcode.EXTENDED_ARG, arg >> 16, self._opcode, arg & 0xFFFF
|
||||
)
|
||||
else:
|
||||
b = struct.pack("<BH", self._opcode, arg)
|
||||
|
||||
if self._extended_args:
|
||||
while len(b) < self._size:
|
||||
b = struct.pack("<BH", _opcode.EXTENDED_ARG, 0) + b
|
||||
|
||||
return b
|
||||
return bytes(b)
|
||||
|
||||
@classmethod
|
||||
def disassemble(cls, lineno, code, offset):
|
||||
op = code[offset]
|
||||
index = 2 * offset if OFFSET_AS_INSTRUCTION else offset
|
||||
op = code[index]
|
||||
if op >= _opcode.HAVE_ARGUMENT:
|
||||
if _WORDCODE:
|
||||
arg = code[offset + 1]
|
||||
else:
|
||||
arg = code[offset + 1] + code[offset + 2] * 256
|
||||
arg = code[index + 1]
|
||||
else:
|
||||
arg = UNSET
|
||||
name = _opcode.opname[op]
|
||||
# fabioz: added offset to ConcreteBytecode
|
||||
# Need to keep an eye on https://github.com/MatthieuDartiailh/bytecode/issues/48 in
|
||||
# case the library decides to add this in some other way.
|
||||
return cls(name, arg, lineno=lineno, offset=offset)
|
||||
return cls(name, arg, lineno=lineno, offset=index)
|
||||
|
||||
|
||||
class ConcreteBytecode(_bytecode._BaseBytecodeList):
|
||||
|
|
@ -205,14 +177,15 @@ class ConcreteBytecode(_bytecode._BaseBytecodeList):
|
|||
instructions = []
|
||||
offset = 0
|
||||
lineno = code.co_firstlineno
|
||||
while offset < len(code.co_code):
|
||||
if offset in line_starts:
|
||||
lineno = line_starts[offset]
|
||||
while offset < (len(code.co_code) // (2 if OFFSET_AS_INSTRUCTION else 1)):
|
||||
lineno_off = (2 * offset) if OFFSET_AS_INSTRUCTION else offset
|
||||
if lineno_off in line_starts:
|
||||
lineno = line_starts[lineno_off]
|
||||
|
||||
instr = ConcreteInstr.disassemble(lineno, code.co_code, offset)
|
||||
|
||||
instructions.append(instr)
|
||||
offset += instr.size
|
||||
offset += (instr.size // 2) if OFFSET_AS_INSTRUCTION else instr.size
|
||||
|
||||
bytecode = ConcreteBytecode()
|
||||
|
||||
|
|
@ -262,8 +235,11 @@ class ConcreteBytecode(_bytecode._BaseBytecodeList):
|
|||
linenos = []
|
||||
for lineno, instr in self._normalize_lineno(self, self.first_lineno):
|
||||
code_str.append(instr.assemble())
|
||||
linenos.append((offset, lineno))
|
||||
offset += instr.size
|
||||
i_size = instr.size
|
||||
linenos.append(
|
||||
((offset * 2) if OFFSET_AS_INSTRUCTION else offset, i_size, lineno)
|
||||
)
|
||||
offset += (i_size // 2) if OFFSET_AS_INSTRUCTION else i_size
|
||||
code_str = b"".join(code_str)
|
||||
return (code_str, linenos)
|
||||
|
||||
|
|
@ -272,7 +248,7 @@ class ConcreteBytecode(_bytecode._BaseBytecodeList):
|
|||
lnotab = []
|
||||
old_offset = 0
|
||||
old_lineno = first_lineno
|
||||
for offset, lineno in linenos:
|
||||
for offset, _, lineno in linenos:
|
||||
dlineno = lineno - old_lineno
|
||||
if dlineno == 0:
|
||||
continue
|
||||
|
|
@ -291,11 +267,13 @@ class ConcreteBytecode(_bytecode._BaseBytecodeList):
|
|||
doff -= 255
|
||||
|
||||
while dlineno < -128:
|
||||
lnotab.append(struct.pack("Bb", 0, -128))
|
||||
lnotab.append(struct.pack("Bb", doff, -128))
|
||||
doff = 0
|
||||
dlineno -= -128
|
||||
|
||||
while dlineno > 127:
|
||||
lnotab.append(struct.pack("Bb", 0, 127))
|
||||
lnotab.append(struct.pack("Bb", doff, 127))
|
||||
doff = 0
|
||||
dlineno -= 127
|
||||
|
||||
assert 0 <= doff <= 255
|
||||
|
|
@ -305,6 +283,53 @@ class ConcreteBytecode(_bytecode._BaseBytecodeList):
|
|||
|
||||
return b"".join(lnotab)
|
||||
|
||||
@staticmethod
|
||||
def _pack_linetable(doff, dlineno):
|
||||
linetable = []
|
||||
while doff > 254:
|
||||
linetable.append(b"\xfe\x00")
|
||||
doff -= 254
|
||||
|
||||
while dlineno < -127:
|
||||
linetable.append(struct.pack("Bb", 0, -127))
|
||||
dlineno -= -127
|
||||
|
||||
while dlineno > 127:
|
||||
linetable.append(struct.pack("Bb", 0, 127))
|
||||
dlineno -= 127
|
||||
|
||||
assert 0 <= doff <= 254
|
||||
assert -127 <= dlineno <= 127
|
||||
|
||||
linetable.append(struct.pack("Bb", doff, dlineno))
|
||||
return linetable
|
||||
|
||||
def _assemble_linestable(self, first_lineno, linenos):
|
||||
if not linenos:
|
||||
return b""
|
||||
|
||||
linetable = []
|
||||
old_offset = 0
|
||||
offset, i_size, old_lineno = linenos[0]
|
||||
old_dlineno = old_lineno - first_lineno
|
||||
for offset, i_size, lineno in linenos[1:]:
|
||||
dlineno = lineno - old_lineno
|
||||
if dlineno == 0:
|
||||
continue
|
||||
old_lineno = lineno
|
||||
|
||||
doff = offset - old_offset
|
||||
old_offset = offset
|
||||
|
||||
linetable.extend(self._pack_linetable(doff, old_dlineno))
|
||||
old_dlineno = dlineno
|
||||
|
||||
# Pack the line of the last instruction.
|
||||
doff = offset + i_size - old_offset
|
||||
linetable.extend(self._pack_linetable(doff, old_dlineno))
|
||||
|
||||
return b"".join(linetable)
|
||||
|
||||
@staticmethod
|
||||
def _remove_extended_args(instructions):
|
||||
# replace jump targets with blocks
|
||||
|
|
@ -327,8 +352,6 @@ class ConcreteBytecode(_bytecode._BaseBytecodeList):
|
|||
if instr.name == "EXTENDED_ARG":
|
||||
nb_extended_args += 1
|
||||
if extended_arg is not None:
|
||||
if not _WORDCODE:
|
||||
raise ValueError("EXTENDED_ARG followed " "by EXTENDED_ARG")
|
||||
extended_arg = (extended_arg << 8) + instr.arg
|
||||
else:
|
||||
extended_arg = instr.arg
|
||||
|
|
@ -337,10 +360,7 @@ class ConcreteBytecode(_bytecode._BaseBytecodeList):
|
|||
continue
|
||||
|
||||
if extended_arg is not None:
|
||||
if _WORDCODE:
|
||||
arg = (extended_arg << 8) + instr.arg
|
||||
else:
|
||||
arg = (extended_arg << 16) + instr.arg
|
||||
arg = (extended_arg << 8) + instr.arg
|
||||
extended_arg = None
|
||||
|
||||
instr = ConcreteInstr(
|
||||
|
|
@ -348,7 +368,7 @@ class ConcreteBytecode(_bytecode._BaseBytecodeList):
|
|||
arg,
|
||||
lineno=instr.lineno,
|
||||
extended_args=nb_extended_args,
|
||||
offset=instr.offset
|
||||
offset=instr.offset,
|
||||
)
|
||||
instructions[index] = instr
|
||||
nb_extended_args = 0
|
||||
|
|
@ -365,7 +385,11 @@ class ConcreteBytecode(_bytecode._BaseBytecodeList):
|
|||
|
||||
def to_code(self, stacksize=None, *, check_pre_and_post=True):
|
||||
code_str, linenos = self._assemble_code()
|
||||
lnotab = self._assemble_lnotab(self.first_lineno, linenos)
|
||||
lnotab = (
|
||||
self._assemble_linestable(self.first_lineno, linenos)
|
||||
if sys.version_info >= (3, 10)
|
||||
else self._assemble_lnotab(self.first_lineno, linenos)
|
||||
)
|
||||
nlocals = len(self.varnames)
|
||||
if stacksize is None:
|
||||
stacksize = self.compute_stacksize(check_pre_and_post=check_pre_and_post)
|
||||
|
|
@ -423,7 +447,7 @@ class ConcreteBytecode(_bytecode._BaseBytecodeList):
|
|||
target = instr.get_jump_target(offset)
|
||||
if target is not None:
|
||||
jump_targets.add(target)
|
||||
offset += instr.size
|
||||
offset += (instr.size // 2) if OFFSET_AS_INSTRUCTION else instr.size
|
||||
|
||||
# create labels
|
||||
jumps = []
|
||||
|
|
@ -464,7 +488,7 @@ class ConcreteBytecode(_bytecode._BaseBytecodeList):
|
|||
else:
|
||||
instr_index = len(instructions)
|
||||
instructions.append(instr)
|
||||
offset += size
|
||||
offset += (size // 2) if OFFSET_AS_INSTRUCTION else size
|
||||
|
||||
if jump_target is not None:
|
||||
jumps.append((instr_index, jump_target))
|
||||
|
|
@ -586,7 +610,7 @@ class _ConvertBytecodeToConcrete:
|
|||
offset = 0
|
||||
for index, instr in enumerate(self.instructions):
|
||||
offsets.append(offset)
|
||||
offset += instr.size
|
||||
offset += instr.size // 2 if OFFSET_AS_INSTRUCTION else instr.size
|
||||
# needed if a label is at the end
|
||||
offsets.append(offset)
|
||||
|
||||
|
|
@ -598,7 +622,9 @@ class _ConvertBytecodeToConcrete:
|
|||
|
||||
if instr.opcode in _opcode.hasjrel:
|
||||
instr_offset = offsets[index]
|
||||
target_offset -= instr_offset + instr.size
|
||||
target_offset -= instr_offset + (
|
||||
instr.size // 2 if OFFSET_AS_INSTRUCTION else instr.size
|
||||
)
|
||||
|
||||
old_size = instr.size
|
||||
# FIXME: better error report if target_offset is negative
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
# alias to keep the 'bytecode' variable free
|
||||
import sys
|
||||
from enum import IntFlag
|
||||
from _pydevd_frame_eval.vendored import bytecode as _bytecode
|
||||
|
||||
try:
|
||||
from enum import IntFlag
|
||||
except ImportError:
|
||||
from aenum import IntFlag
|
||||
|
||||
|
||||
class CompilerFlags(IntFlag):
|
||||
"""Possible values of the co_flags attribute of Code object.
|
||||
|
|
@ -33,7 +30,14 @@ class CompilerFlags(IntFlag):
|
|||
ASYNC_GENERATOR = 0x00200 # noqa
|
||||
|
||||
# __future__ flags
|
||||
FUTURE_GENERATOR_STOP = 0x80000 # noqa
|
||||
# future flags changed in Python 3.9
|
||||
if sys.version_info < (3, 9):
|
||||
FUTURE_GENERATOR_STOP = 0x80000 # noqa
|
||||
if sys.version_info > (3, 6):
|
||||
FUTURE_ANNOTATIONS = 0x100000
|
||||
else:
|
||||
FUTURE_GENERATOR_STOP = 0x800000 # noqa
|
||||
FUTURE_ANNOTATIONS = 0x1000000
|
||||
|
||||
|
||||
def infer_flags(bytecode, is_async=None):
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ def _pushes_back(opname):
|
|||
return (
|
||||
opname.startswith("UNARY_")
|
||||
or opname.startswith("GET_")
|
||||
# BUILD_XXX_UNPACK have been removed in 3.9
|
||||
or opname.startswith("BINARY_")
|
||||
or opname.startswith("INPLACE_")
|
||||
or opname.startswith("BUILD_")
|
||||
|
|
@ -318,14 +319,15 @@ class Instr:
|
|||
def pre_and_post_stack_effect(self, jump=None):
|
||||
_effect = self.stack_effect(jump=jump)
|
||||
|
||||
# To compute pre size and post size to avoid segfault cause by not enough stack element
|
||||
# To compute pre size and post size to avoid segfault cause by not enough
|
||||
# stack element
|
||||
_opname = _opcode.opname[self._opcode]
|
||||
if _opname.startswith("DUP_TOP"):
|
||||
return _effect * -1, _effect * 2
|
||||
if _pushes_back(_opname):
|
||||
# if the op pushes value back to the stack, then the stack effect given by dis.stack_effect
|
||||
# actually equals pre + post effect, therefore we need -1 from the stack effect as a pre
|
||||
# condition
|
||||
# if the op pushes value back to the stack, then the stack effect given
|
||||
# by dis.stack_effect actually equals pre + post effect, therefore we need
|
||||
# -1 from the stack effect as a pre condition
|
||||
return _effect - 1, 1
|
||||
if _opname.startswith("UNPACK_"):
|
||||
# Instr(UNPACK_* , n) pops 1 and pushes n
|
||||
|
|
@ -333,8 +335,11 @@ class Instr:
|
|||
# hence we return -1, _effect + 1
|
||||
return -1, _effect + 1
|
||||
if _opname == "FOR_ITER" and not jump:
|
||||
# Since FOR_ITER needs TOS to be an iterator, which basically means a prerequisite of 1 on the stack
|
||||
# Since FOR_ITER needs TOS to be an iterator, which basically means
|
||||
# a prerequisite of 1 on the stack
|
||||
return -1, 2
|
||||
if _opname == "ROT_N":
|
||||
return (-self._arg, self._arg)
|
||||
return {"ROT_TWO": (-2, 2), "ROT_THREE": (-3, 3), "ROT_FOUR": (-4, 4)}.get(
|
||||
_opname, (_effect, 0)
|
||||
)
|
||||
|
|
@ -381,6 +386,7 @@ class Instr:
|
|||
if self._name in {
|
||||
"RETURN_VALUE",
|
||||
"RAISE_VARARGS",
|
||||
"RERAISE",
|
||||
"BREAK_LOOP",
|
||||
"CONTINUE_LOOP",
|
||||
}:
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@ from _pydevd_frame_eval.vendored.bytecode import (
|
|||
ConcreteBytecode,
|
||||
)
|
||||
|
||||
WORDCODE = sys.version_info >= (3, 6)
|
||||
|
||||
|
||||
def _format_instr_list(block, labels, lineno):
|
||||
instr_list = []
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
#!/bin/python
|
||||
#author: tobias mueller 13.6.13
|
||||
#byteplay test
|
||||
|
||||
from sys import version_info
|
||||
from dis import dis
|
||||
from _pydevd_frame_eval.vendored.bytecode import Bytecode, ConcreteBytecode, dump_bytecode
|
||||
from pprint import pprint
|
||||
|
||||
def f(a, b):
|
||||
# res = a + b
|
||||
return
|
||||
|
||||
def g(a, b):
|
||||
res = a + b if a < b else b + a
|
||||
r = 0
|
||||
for a in range(res):
|
||||
r += 1
|
||||
return r or 2
|
||||
|
||||
for x in (f, g):
|
||||
#get byte code for f
|
||||
dis(x)
|
||||
print(f.__code__.co_code)
|
||||
c = Bytecode.from_code(x.__code__)
|
||||
cc = ConcreteBytecode.from_code(x.__code__)
|
||||
dump_bytecode(c)
|
||||
dump_bytecode(cc)
|
||||
|
||||
#generate byte code
|
||||
cnew = c.to_code()
|
||||
|
||||
x.__code__ = cnew
|
||||
dis(x)
|
||||
|
||||
print(x(3,5))
|
||||
|
|
@ -138,22 +138,41 @@ class BytecodeTests(TestCase):
|
|||
bytecode = Bytecode.from_code(code)
|
||||
label_else = Label()
|
||||
label_exit = Label()
|
||||
self.assertEqual(
|
||||
bytecode,
|
||||
[
|
||||
Instr("LOAD_NAME", "test", lineno=1),
|
||||
Instr("POP_JUMP_IF_FALSE", label_else, lineno=1),
|
||||
Instr("LOAD_CONST", 1, lineno=2),
|
||||
Instr("STORE_NAME", "x", lineno=2),
|
||||
Instr("JUMP_FORWARD", label_exit, lineno=2),
|
||||
label_else,
|
||||
Instr("LOAD_CONST", 2, lineno=4),
|
||||
Instr("STORE_NAME", "x", lineno=4),
|
||||
label_exit,
|
||||
Instr("LOAD_CONST", None, lineno=4),
|
||||
Instr("RETURN_VALUE", lineno=4),
|
||||
],
|
||||
)
|
||||
if sys.version_info < (3, 10):
|
||||
self.assertEqual(
|
||||
bytecode,
|
||||
[
|
||||
Instr("LOAD_NAME", "test", lineno=1),
|
||||
Instr("POP_JUMP_IF_FALSE", label_else, lineno=1),
|
||||
Instr("LOAD_CONST", 1, lineno=2),
|
||||
Instr("STORE_NAME", "x", lineno=2),
|
||||
Instr("JUMP_FORWARD", label_exit, lineno=2),
|
||||
label_else,
|
||||
Instr("LOAD_CONST", 2, lineno=4),
|
||||
Instr("STORE_NAME", "x", lineno=4),
|
||||
label_exit,
|
||||
Instr("LOAD_CONST", None, lineno=4),
|
||||
Instr("RETURN_VALUE", lineno=4),
|
||||
],
|
||||
)
|
||||
# Control flow handling appears to have changed under Python 3.10
|
||||
else:
|
||||
self.assertEqual(
|
||||
bytecode,
|
||||
[
|
||||
Instr("LOAD_NAME", "test", lineno=1),
|
||||
Instr("POP_JUMP_IF_FALSE", label_else, lineno=1),
|
||||
Instr("LOAD_CONST", 1, lineno=2),
|
||||
Instr("STORE_NAME", "x", lineno=2),
|
||||
Instr("LOAD_CONST", None, lineno=2),
|
||||
Instr("RETURN_VALUE", lineno=2),
|
||||
label_else,
|
||||
Instr("LOAD_CONST", 2, lineno=4),
|
||||
Instr("STORE_NAME", "x", lineno=4),
|
||||
Instr("LOAD_CONST", None, lineno=4),
|
||||
Instr("RETURN_VALUE", lineno=4),
|
||||
],
|
||||
)
|
||||
|
||||
def test_from_code_freevars(self):
|
||||
ns = {}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ from _pydevd_frame_eval.vendored.bytecode import (
|
|||
BasicBlock,
|
||||
ControlFlowGraph,
|
||||
)
|
||||
from _pydevd_frame_eval.vendored.bytecode.tests import disassemble as _disassemble, TestCase, WORDCODE
|
||||
from _pydevd_frame_eval.vendored.bytecode.concrete import OFFSET_AS_INSTRUCTION
|
||||
from _pydevd_frame_eval.vendored.bytecode.tests import disassemble as _disassemble, TestCase
|
||||
|
||||
|
||||
def disassemble(
|
||||
|
|
@ -566,20 +567,14 @@ class BytecodeBlocksFunctionalTests(TestCase):
|
|||
]
|
||||
)
|
||||
|
||||
if WORDCODE:
|
||||
if OFFSET_AS_INSTRUCTION:
|
||||
# The argument of the jump is divided by 2
|
||||
expected = (
|
||||
b"|\x05" b"r\x08" b"|\x00" b"}\x05" b"d\x01" b"}\x05" b"|\x05" b"S\x00"
|
||||
b"|\x05" b"r\x04" b"|\x00" b"}\x05" b"d\x01" b"}\x05" b"|\x05" b"S\x00"
|
||||
)
|
||||
else:
|
||||
expected = (
|
||||
b"|\x05\x00"
|
||||
b"r\x0c\x00"
|
||||
b"|\x00\x00"
|
||||
b"}\x05\x00"
|
||||
b"d\x01\x00"
|
||||
b"}\x05\x00"
|
||||
b"|\x05\x00"
|
||||
b"S"
|
||||
b"|\x05" b"r\x08" b"|\x00" b"}\x05" b"d\x01" b"}\x05" b"|\x05" b"S\x00"
|
||||
)
|
||||
|
||||
code = bytecode.to_code()
|
||||
|
|
|
|||
|
|
@ -68,6 +68,26 @@ class CodeTests(unittest.TestCase):
|
|||
function=True,
|
||||
)
|
||||
|
||||
# Added because Python 3.10 added some special beahavior with respect to
|
||||
# generators in term of stack size
|
||||
def test_generator_func(self):
|
||||
self.check(
|
||||
"""
|
||||
def func(arg, arg2):
|
||||
yield
|
||||
""",
|
||||
function=True,
|
||||
)
|
||||
|
||||
def test_async_func(self):
|
||||
self.check(
|
||||
"""
|
||||
async def func(arg, arg2):
|
||||
pass
|
||||
""",
|
||||
function=True,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@ pytestmark = pytest.mark.skipif(not IS_PY36_OR_GREATER or not IS_CPYTHON or not
|
|||
#!/usr/bin/env python3
|
||||
import opcode
|
||||
import sys
|
||||
import textwrap
|
||||
import types
|
||||
import unittest
|
||||
import textwrap
|
||||
|
||||
from _pydevd_frame_eval.vendored.bytecode import (
|
||||
UNSET,
|
||||
Label,
|
||||
|
|
@ -21,7 +22,8 @@ from _pydevd_frame_eval.vendored.bytecode import (
|
|||
ConcreteInstr,
|
||||
ConcreteBytecode,
|
||||
)
|
||||
from _pydevd_frame_eval.vendored.bytecode.tests import get_code, TestCase, WORDCODE
|
||||
from _pydevd_frame_eval.vendored.bytecode.concrete import OFFSET_AS_INSTRUCTION
|
||||
from _pydevd_frame_eval.vendored.bytecode.tests import get_code, TestCase
|
||||
|
||||
|
||||
class ConcreteInstrTests(TestCase):
|
||||
|
|
@ -62,7 +64,7 @@ class ConcreteInstrTests(TestCase):
|
|||
self.assertEqual(instr.opcode, 100)
|
||||
self.assertEqual(instr.arg, 5)
|
||||
self.assertEqual(instr.lineno, 12)
|
||||
self.assertEqual(instr.size, 2 if WORDCODE else 3)
|
||||
self.assertEqual(instr.size, 2)
|
||||
|
||||
def test_set(self):
|
||||
instr = ConcreteInstr("LOAD_CONST", 5, lineno=3)
|
||||
|
|
@ -103,12 +105,12 @@ class ConcreteInstrTests(TestCase):
|
|||
# extended argument
|
||||
instr.arg = 0x1234ABCD
|
||||
self.assertEqual(instr.arg, 0x1234ABCD)
|
||||
self.assertEqual(instr.size, 8 if WORDCODE else 6)
|
||||
self.assertEqual(instr.size, 8)
|
||||
|
||||
# small argument
|
||||
instr.arg = 0
|
||||
self.assertEqual(instr.arg, 0)
|
||||
self.assertEqual(instr.size, 2 if WORDCODE else 3)
|
||||
self.assertEqual(instr.size, 2)
|
||||
|
||||
# invalid argument
|
||||
self.assertRaises(ValueError, setattr, instr, "arg", -1)
|
||||
|
|
@ -124,44 +126,40 @@ class ConcreteInstrTests(TestCase):
|
|||
self.assertRaises(ValueError, setattr, instr, "lineno", -1)
|
||||
|
||||
def test_size(self):
|
||||
self.assertEqual(ConcreteInstr("ROT_TWO").size, 2 if WORDCODE else 1)
|
||||
self.assertEqual(ConcreteInstr("LOAD_CONST", 3).size, 2 if WORDCODE else 3)
|
||||
self.assertEqual(
|
||||
ConcreteInstr("LOAD_CONST", 0x1234ABCD).size, 8 if WORDCODE else 6
|
||||
)
|
||||
self.assertEqual(ConcreteInstr("ROT_TWO").size, 2)
|
||||
self.assertEqual(ConcreteInstr("LOAD_CONST", 3).size, 2)
|
||||
self.assertEqual(ConcreteInstr("LOAD_CONST", 0x1234ABCD).size, 8)
|
||||
|
||||
def test_disassemble(self):
|
||||
code = b"\t\x00d\x03" if WORDCODE else b"\td\x03\x00"
|
||||
code = b"\t\x00d\x03"
|
||||
instr = ConcreteInstr.disassemble(1, code, 0)
|
||||
self.assertEqual(instr, ConcreteInstr("NOP", lineno=1))
|
||||
|
||||
instr = ConcreteInstr.disassemble(2, code, 2 if WORDCODE else 1)
|
||||
instr = ConcreteInstr.disassemble(2, code, 1 if OFFSET_AS_INSTRUCTION else 2)
|
||||
self.assertEqual(instr, ConcreteInstr("LOAD_CONST", 3, lineno=2))
|
||||
|
||||
code = b"\x90\x12\x904\x90\xabd\xcd" if WORDCODE else b"\x904\x12d\xcd\xab"
|
||||
code = b"\x90\x12\x904\x90\xabd\xcd"
|
||||
|
||||
instr = ConcreteInstr.disassemble(3, code, 0)
|
||||
self.assertEqual(
|
||||
instr, ConcreteInstr("EXTENDED_ARG", 0x12 if WORDCODE else 0x1234, lineno=3)
|
||||
)
|
||||
self.assertEqual(instr, ConcreteInstr("EXTENDED_ARG", 0x12, lineno=3))
|
||||
|
||||
def test_assemble(self):
|
||||
instr = ConcreteInstr("NOP")
|
||||
self.assertEqual(instr.assemble(), b"\t\x00" if WORDCODE else b"\t")
|
||||
self.assertEqual(instr.assemble(), b"\t\x00")
|
||||
|
||||
instr = ConcreteInstr("LOAD_CONST", 3)
|
||||
self.assertEqual(instr.assemble(), b"d\x03" if WORDCODE else b"d\x03\x00")
|
||||
self.assertEqual(instr.assemble(), b"d\x03")
|
||||
|
||||
instr = ConcreteInstr("LOAD_CONST", 0x1234ABCD)
|
||||
self.assertEqual(
|
||||
instr.assemble(),
|
||||
(b"\x90\x12\x904\x90\xabd\xcd" if WORDCODE else b"\x904\x12d\xcd\xab"),
|
||||
(b"\x90\x12\x904\x90\xabd\xcd"),
|
||||
)
|
||||
|
||||
instr = ConcreteInstr("LOAD_CONST", 3, extended_args=1)
|
||||
self.assertEqual(
|
||||
instr.assemble(),
|
||||
(b"\x90\x00d\x03" if WORDCODE else b"\x90\x00\x00d\x03\x00"),
|
||||
(b"\x90\x00d\x03"),
|
||||
)
|
||||
|
||||
def test_get_jump_target(self):
|
||||
|
|
@ -169,7 +167,9 @@ class ConcreteInstrTests(TestCase):
|
|||
self.assertEqual(jump_abs.get_jump_target(100), 3)
|
||||
|
||||
jump_forward = ConcreteInstr("JUMP_FORWARD", 5)
|
||||
self.assertEqual(jump_forward.get_jump_target(10), 17 if WORDCODE else 18)
|
||||
self.assertEqual(
|
||||
jump_forward.get_jump_target(10), 16 if OFFSET_AS_INSTRUCTION else 17
|
||||
)
|
||||
|
||||
|
||||
class ConcreteBytecodeTests(TestCase):
|
||||
|
|
@ -241,43 +241,42 @@ class ConcreteBytecodeTests(TestCase):
|
|||
ConcreteBytecode([Label()])
|
||||
|
||||
def test_to_code_lnotab(self):
|
||||
# x = 7
|
||||
# y = 8
|
||||
# z = 9
|
||||
|
||||
# We use an actual function for the simple case to
|
||||
# ensure we get lnotab right
|
||||
def f():
|
||||
#
|
||||
#
|
||||
x = 7 # noqa
|
||||
y = 8 # noqa
|
||||
z = 9 # noqa
|
||||
|
||||
fl = f.__code__.co_firstlineno
|
||||
concrete = ConcreteBytecode()
|
||||
concrete.consts = [7, 8, 9]
|
||||
concrete.names = ["x", "y", "z"]
|
||||
concrete.first_lineno = 3
|
||||
concrete.consts = [None, 7, 8, 9]
|
||||
concrete.varnames = ["x", "y", "z"]
|
||||
concrete.first_lineno = fl
|
||||
concrete.extend(
|
||||
[
|
||||
ConcreteInstr("LOAD_CONST", 0),
|
||||
ConcreteInstr("STORE_NAME", 0),
|
||||
SetLineno(4),
|
||||
SetLineno(fl + 3),
|
||||
ConcreteInstr("LOAD_CONST", 1),
|
||||
ConcreteInstr("STORE_NAME", 1),
|
||||
SetLineno(5),
|
||||
ConcreteInstr("STORE_FAST", 0),
|
||||
SetLineno(fl + 4),
|
||||
ConcreteInstr("LOAD_CONST", 2),
|
||||
ConcreteInstr("STORE_NAME", 2),
|
||||
ConcreteInstr("STORE_FAST", 1),
|
||||
SetLineno(fl + 5),
|
||||
ConcreteInstr("LOAD_CONST", 3),
|
||||
ConcreteInstr("STORE_FAST", 2),
|
||||
ConcreteInstr("LOAD_CONST", 0),
|
||||
ConcreteInstr("RETURN_VALUE"),
|
||||
]
|
||||
)
|
||||
|
||||
code = concrete.to_code()
|
||||
if WORDCODE:
|
||||
expected = b"d\x00Z\x00d\x01Z\x01d\x02Z\x02"
|
||||
else:
|
||||
expected = (
|
||||
b"d\x00\x00"
|
||||
b"Z\x00\x00"
|
||||
b"d\x01\x00"
|
||||
b"Z\x01\x00"
|
||||
b"d\x02\x00"
|
||||
b"Z\x02\x00"
|
||||
)
|
||||
self.assertEqual(code.co_code, expected)
|
||||
self.assertEqual(code.co_firstlineno, 3)
|
||||
self.assertEqual(
|
||||
code.co_lnotab, b"\x04\x01\x04\x01" if WORDCODE else b"\x06\x01\x06\x01"
|
||||
)
|
||||
self.assertEqual(code.co_code, f.__code__.co_code)
|
||||
self.assertEqual(code.co_lnotab, f.__code__.co_lnotab)
|
||||
if sys.version_info >= (3, 10):
|
||||
self.assertEqual(code.co_linetable, f.__code__.co_linetable)
|
||||
|
||||
def test_negative_lnotab(self):
|
||||
# x = 7
|
||||
|
|
@ -296,22 +295,15 @@ class ConcreteBytecodeTests(TestCase):
|
|||
concrete.names = ["x", "y"]
|
||||
concrete.first_lineno = 5
|
||||
|
||||
if sys.version_info >= (3, 6):
|
||||
code = concrete.to_code()
|
||||
expected = b"d\x00Z\x00d\x01Z\x01"
|
||||
self.assertEqual(code.co_code, expected)
|
||||
self.assertEqual(code.co_firstlineno, 5)
|
||||
self.assertEqual(code.co_lnotab, b"\x04\xfd")
|
||||
else:
|
||||
with self.assertRaises(ValueError) as cm:
|
||||
code = concrete.to_code()
|
||||
self.assertEqual(
|
||||
str(cm.exception),
|
||||
"negative line number delta is not supported " "on Python < 3.6",
|
||||
)
|
||||
code = concrete.to_code()
|
||||
expected = b"d\x00Z\x00d\x01Z\x01"
|
||||
self.assertEqual(code.co_code, expected)
|
||||
self.assertEqual(code.co_firstlineno, 5)
|
||||
self.assertEqual(code.co_lnotab, b"\x04\xfd")
|
||||
|
||||
def test_extended_lnotab(self):
|
||||
# x = 7
|
||||
# 200 blank lines
|
||||
# y = 8
|
||||
concrete = ConcreteBytecode(
|
||||
[
|
||||
|
|
@ -329,21 +321,38 @@ class ConcreteBytecodeTests(TestCase):
|
|||
concrete.names = ["x", "y"]
|
||||
concrete.first_lineno = 1
|
||||
|
||||
if sys.version_info >= (3, 6):
|
||||
code = concrete.to_code()
|
||||
expected = b"d\x00Z\x00d\x01Z\x01"
|
||||
self.assertEqual(code.co_code, expected)
|
||||
self.assertEqual(code.co_firstlineno, 1)
|
||||
self.assertEqual(
|
||||
code.co_lnotab, b"\x00\x7f\x02\x01\x02\x01\x00\x80\x02\xff"
|
||||
)
|
||||
else:
|
||||
with self.assertRaises(ValueError) as cm:
|
||||
code = concrete.to_code()
|
||||
self.assertEqual(
|
||||
str(cm.exception),
|
||||
"negative line number delta is not supported " "on Python < 3.6",
|
||||
)
|
||||
code = concrete.to_code()
|
||||
expected = b"d\x00Z\x00d\x01Z\x01"
|
||||
self.assertEqual(code.co_code, expected)
|
||||
self.assertEqual(code.co_firstlineno, 1)
|
||||
self.assertEqual(code.co_lnotab, b"\x02\x7f\x00\x01\x02\x01\x02\x80\x00\xff")
|
||||
|
||||
def test_extended_lnotab2(self):
|
||||
# x = 7
|
||||
# 200 blank lines
|
||||
# y = 8
|
||||
base_code = compile("x = 7" + "\n" * 200 + "y = 8", "", "exec")
|
||||
concrete = ConcreteBytecode(
|
||||
[
|
||||
ConcreteInstr("LOAD_CONST", 0),
|
||||
ConcreteInstr("STORE_NAME", 0),
|
||||
SetLineno(201),
|
||||
ConcreteInstr("LOAD_CONST", 1),
|
||||
ConcreteInstr("STORE_NAME", 1),
|
||||
ConcreteInstr("LOAD_CONST", 2),
|
||||
ConcreteInstr("RETURN_VALUE"),
|
||||
]
|
||||
)
|
||||
concrete.consts = [None, 7, 8]
|
||||
concrete.names = ["x", "y"]
|
||||
concrete.first_lineno = 1
|
||||
|
||||
code = concrete.to_code()
|
||||
self.assertEqual(code.co_code, base_code.co_code)
|
||||
self.assertEqual(code.co_firstlineno, base_code.co_firstlineno)
|
||||
self.assertEqual(code.co_lnotab, base_code.co_lnotab)
|
||||
if sys.version_info >= (3, 10):
|
||||
self.assertEqual(code.co_linetable, base_code.co_linetable)
|
||||
|
||||
def test_to_bytecode_consts(self):
|
||||
# x = -0.0
|
||||
|
|
@ -476,7 +485,7 @@ class ConcreteBytecodeTests(TestCase):
|
|||
self.assertEqual(code.co_cellvars, ("__class__",))
|
||||
self.assertEqual(
|
||||
code.co_code,
|
||||
b"\x94\x01\x89\x01" if WORDCODE else b"\x94\x01\x00\x89\x01\x00",
|
||||
b"\x94\x01\x89\x01",
|
||||
)
|
||||
|
||||
def test_explicit_stacksize(self):
|
||||
|
|
@ -571,7 +580,7 @@ class ConcreteBytecodeTests(TestCase):
|
|||
class ConcreteFromCodeTests(TestCase):
|
||||
def test_extended_arg(self):
|
||||
# Create a code object from arbitrary bytecode
|
||||
co_code = b"\x90\x12\x904\x90\xabd\xcd" if WORDCODE else b"\x904\x12d\xcd\xab"
|
||||
co_code = b"\x90\x12\x904\x90\xabd\xcd"
|
||||
code = get_code("x=1")
|
||||
args = (
|
||||
(code.co_argcount,)
|
||||
|
|
@ -590,7 +599,7 @@ class ConcreteFromCodeTests(TestCase):
|
|||
code.co_filename,
|
||||
code.co_name,
|
||||
code.co_firstlineno,
|
||||
code.co_lnotab,
|
||||
code.co_linetable if sys.version_info >= (3, 10) else code.co_lnotab,
|
||||
code.co_freevars,
|
||||
code.co_cellvars,
|
||||
)
|
||||
|
|
@ -605,78 +614,106 @@ class ConcreteFromCodeTests(TestCase):
|
|||
|
||||
# with EXTENDED_ARG opcode
|
||||
bytecode = ConcreteBytecode.from_code(code, extended_arg=True)
|
||||
if WORDCODE:
|
||||
expected = [
|
||||
ConcreteInstr("EXTENDED_ARG", 0x12, lineno=1),
|
||||
ConcreteInstr("EXTENDED_ARG", 0x34, lineno=1),
|
||||
ConcreteInstr("EXTENDED_ARG", 0xAB, lineno=1),
|
||||
ConcreteInstr("LOAD_CONST", 0xCD, lineno=1),
|
||||
]
|
||||
else:
|
||||
expected = [
|
||||
ConcreteInstr("EXTENDED_ARG", 0x1234, lineno=1),
|
||||
ConcreteInstr("LOAD_CONST", 0xABCD, lineno=1),
|
||||
]
|
||||
expected = [
|
||||
ConcreteInstr("EXTENDED_ARG", 0x12, lineno=1),
|
||||
ConcreteInstr("EXTENDED_ARG", 0x34, lineno=1),
|
||||
ConcreteInstr("EXTENDED_ARG", 0xAB, lineno=1),
|
||||
ConcreteInstr("LOAD_CONST", 0xCD, lineno=1),
|
||||
]
|
||||
self.assertListEqual(list(bytecode), expected)
|
||||
|
||||
def test_extended_arg_make_function(self):
|
||||
code_obj = get_code(
|
||||
"""
|
||||
def foo(x: int, y: int):
|
||||
pass
|
||||
"""
|
||||
)
|
||||
if (3, 9) <= sys.version_info < (3, 10):
|
||||
from _pydevd_frame_eval.vendored.bytecode.tests.util_annotation import get_code as get_code_future
|
||||
|
||||
code_obj = get_code_future(
|
||||
"""
|
||||
def foo(x: int, y: int):
|
||||
pass
|
||||
"""
|
||||
)
|
||||
else:
|
||||
code_obj = get_code(
|
||||
"""
|
||||
def foo(x: int, y: int):
|
||||
pass
|
||||
"""
|
||||
)
|
||||
|
||||
# without EXTENDED_ARG
|
||||
concrete = ConcreteBytecode.from_code(code_obj)
|
||||
func_code = concrete.consts[1]
|
||||
self.assertEqual(concrete.names, ["int", "foo"])
|
||||
self.assertEqual(concrete.consts, [("x", "y"), func_code, "foo", None])
|
||||
if WORDCODE:
|
||||
expected = [
|
||||
ConcreteInstr("LOAD_NAME", 0, lineno=1),
|
||||
ConcreteInstr("LOAD_NAME", 0, lineno=1),
|
||||
if sys.version_info >= (3, 10):
|
||||
func_code = concrete.consts[2]
|
||||
names = ["int", "foo"]
|
||||
consts = ["x", "y", func_code, "foo", None]
|
||||
const_offset = 1
|
||||
name_offset = 1
|
||||
first_instrs = [
|
||||
ConcreteInstr("LOAD_CONST", 0, lineno=1),
|
||||
ConcreteInstr("BUILD_CONST_KEY_MAP", 2, lineno=1),
|
||||
ConcreteInstr("LOAD_NAME", 0, lineno=1),
|
||||
ConcreteInstr("LOAD_CONST", 1, lineno=1),
|
||||
ConcreteInstr("LOAD_CONST", 2, lineno=1),
|
||||
ConcreteInstr("MAKE_FUNCTION", 4, lineno=1),
|
||||
ConcreteInstr("STORE_NAME", 1, lineno=1),
|
||||
ConcreteInstr("LOAD_CONST", 3, lineno=1),
|
||||
ConcreteInstr("RETURN_VALUE", lineno=1),
|
||||
ConcreteInstr("LOAD_NAME", 0, lineno=1),
|
||||
ConcreteInstr("BUILD_TUPLE", 4, lineno=1),
|
||||
]
|
||||
elif (
|
||||
sys.version_info >= (3, 7)
|
||||
and concrete.flags & CompilerFlags.FUTURE_ANNOTATIONS
|
||||
):
|
||||
func_code = concrete.consts[2]
|
||||
names = ["foo"]
|
||||
consts = ["int", ("x", "y"), func_code, "foo", None]
|
||||
const_offset = 1
|
||||
name_offset = 0
|
||||
first_instrs = [
|
||||
ConcreteInstr("LOAD_CONST", 0, lineno=1),
|
||||
ConcreteInstr("LOAD_CONST", 0, lineno=1),
|
||||
ConcreteInstr("LOAD_CONST", 0 + const_offset, lineno=1),
|
||||
ConcreteInstr("BUILD_CONST_KEY_MAP", 2, lineno=1),
|
||||
]
|
||||
else:
|
||||
expected = [
|
||||
func_code = concrete.consts[1]
|
||||
names = ["int", "foo"]
|
||||
consts = [("x", "y"), func_code, "foo", None]
|
||||
const_offset = 0
|
||||
name_offset = 1
|
||||
first_instrs = [
|
||||
ConcreteInstr("LOAD_NAME", 0, lineno=1),
|
||||
ConcreteInstr("LOAD_NAME", 0, lineno=1),
|
||||
ConcreteInstr("LOAD_CONST", 0, lineno=1),
|
||||
ConcreteInstr("LOAD_CONST", 1, lineno=1),
|
||||
ConcreteInstr("LOAD_CONST", 2, lineno=1),
|
||||
ConcreteInstr("MAKE_FUNCTION", 3 << 16, lineno=1),
|
||||
ConcreteInstr("STORE_NAME", 1, lineno=1),
|
||||
ConcreteInstr("LOAD_CONST", 3, lineno=1),
|
||||
ConcreteInstr("RETURN_VALUE", lineno=1),
|
||||
ConcreteInstr("LOAD_CONST", 0 + const_offset, lineno=1),
|
||||
ConcreteInstr("BUILD_CONST_KEY_MAP", 2, lineno=1),
|
||||
]
|
||||
|
||||
self.assertEqual(concrete.names, names)
|
||||
self.assertEqual(concrete.consts, consts)
|
||||
expected = first_instrs + [
|
||||
ConcreteInstr("LOAD_CONST", 1 + const_offset, lineno=1),
|
||||
ConcreteInstr("LOAD_CONST", 2 + const_offset, lineno=1),
|
||||
ConcreteInstr("MAKE_FUNCTION", 4, lineno=1),
|
||||
ConcreteInstr("STORE_NAME", name_offset, lineno=1),
|
||||
ConcreteInstr("LOAD_CONST", 3 + const_offset, lineno=1),
|
||||
ConcreteInstr("RETURN_VALUE", lineno=1),
|
||||
]
|
||||
self.assertListEqual(list(concrete), expected)
|
||||
|
||||
# with EXTENDED_ARG
|
||||
concrete = ConcreteBytecode.from_code(code_obj, extended_arg=True)
|
||||
func_code = concrete.consts[1]
|
||||
self.assertEqual(concrete.names, ["int", "foo"])
|
||||
self.assertEqual(concrete.consts, [("x", "y"), func_code, "foo", None])
|
||||
if not WORDCODE:
|
||||
expected = [
|
||||
ConcreteInstr("LOAD_NAME", 0, lineno=1),
|
||||
ConcreteInstr("LOAD_NAME", 0, lineno=1),
|
||||
ConcreteInstr("LOAD_CONST", 0, lineno=1),
|
||||
ConcreteInstr("LOAD_CONST", 1, lineno=1),
|
||||
ConcreteInstr("LOAD_CONST", 2, lineno=1),
|
||||
ConcreteInstr("EXTENDED_ARG", 3, lineno=1),
|
||||
ConcreteInstr("MAKE_FUNCTION", 0, lineno=1),
|
||||
ConcreteInstr("STORE_NAME", 1, lineno=1),
|
||||
ConcreteInstr("LOAD_CONST", 3, lineno=1),
|
||||
ConcreteInstr("RETURN_VALUE", lineno=1),
|
||||
]
|
||||
# With future annotation the int annotation is stringified and
|
||||
# stored as constant this the default behavior under Python 3.10
|
||||
if sys.version_info >= (3, 10):
|
||||
func_code = concrete.consts[2]
|
||||
names = ["int", "foo"]
|
||||
consts = ["x", "y", func_code, "foo", None]
|
||||
elif concrete.flags & CompilerFlags.FUTURE_ANNOTATIONS:
|
||||
func_code = concrete.consts[2]
|
||||
names = ["foo"]
|
||||
consts = ["int", ("x", "y"), func_code, "foo", None]
|
||||
else:
|
||||
func_code = concrete.consts[1]
|
||||
names = ["int", "foo"]
|
||||
consts = [("x", "y"), func_code, "foo", None]
|
||||
|
||||
self.assertEqual(concrete.names, names)
|
||||
self.assertEqual(concrete.consts, consts)
|
||||
self.assertListEqual(list(concrete), expected)
|
||||
|
||||
# The next three tests ensure we can round trip ConcreteBytecode generated
|
||||
|
|
@ -1093,10 +1130,12 @@ class BytecodeToConcreteTests(TestCase):
|
|||
concrete = bytecode.to_concrete_bytecode()
|
||||
expected = [
|
||||
ConcreteInstr("LOAD_NAME", 0, lineno=1),
|
||||
ConcreteInstr("POP_JUMP_IF_FALSE", 14 if WORDCODE else 21, lineno=1),
|
||||
ConcreteInstr(
|
||||
"POP_JUMP_IF_FALSE", 7 if OFFSET_AS_INSTRUCTION else 14, lineno=1
|
||||
),
|
||||
ConcreteInstr("LOAD_CONST", 0, lineno=2),
|
||||
ConcreteInstr("STORE_NAME", 1, lineno=2),
|
||||
ConcreteInstr("JUMP_FORWARD", 4 if WORDCODE else 6, lineno=2),
|
||||
ConcreteInstr("JUMP_FORWARD", 2 if OFFSET_AS_INSTRUCTION else 4, lineno=2),
|
||||
ConcreteInstr("LOAD_CONST", 1, lineno=4),
|
||||
ConcreteInstr("STORE_NAME", 1, lineno=4),
|
||||
ConcreteInstr("LOAD_CONST", 2, lineno=4),
|
||||
|
|
@ -1232,10 +1271,10 @@ class BytecodeToConcreteTests(TestCase):
|
|||
)
|
||||
|
||||
code_obj = code.to_code()
|
||||
if WORDCODE:
|
||||
expected = b"\x90\x01\x90\x00q\x06" + NOP * nb_nop + b"d\x00S\x00"
|
||||
if OFFSET_AS_INSTRUCTION:
|
||||
expected = b"\x90\x80q\x02" + NOP * nb_nop + b"d\x00S\x00"
|
||||
else:
|
||||
expected = b"\x90\x01\x00q\x06\x00" + NOP * nb_nop + b"d\x00\x00S"
|
||||
expected = b"\x90\x01\x90\x00q\x06" + NOP * nb_nop + b"d\x00S\x00"
|
||||
self.assertEqual(code_obj.co_code, expected)
|
||||
|
||||
def test_jumps(self):
|
||||
|
|
@ -1265,10 +1304,12 @@ class BytecodeToConcreteTests(TestCase):
|
|||
code = code.to_concrete_bytecode()
|
||||
expected = [
|
||||
ConcreteInstr("LOAD_NAME", 0, lineno=1),
|
||||
ConcreteInstr("POP_JUMP_IF_FALSE", 10 if WORDCODE else 15, lineno=1),
|
||||
ConcreteInstr(
|
||||
"POP_JUMP_IF_FALSE", 5 if OFFSET_AS_INSTRUCTION else 10, lineno=1
|
||||
),
|
||||
ConcreteInstr("LOAD_CONST", 0, lineno=2),
|
||||
ConcreteInstr("STORE_NAME", 1, lineno=2),
|
||||
ConcreteInstr("JUMP_FORWARD", 4 if WORDCODE else 6, lineno=2),
|
||||
ConcreteInstr("JUMP_FORWARD", 2 if OFFSET_AS_INSTRUCTION else 4, lineno=2),
|
||||
ConcreteInstr("LOAD_CONST", 1, lineno=4),
|
||||
ConcreteInstr("STORE_NAME", 1, lineno=4),
|
||||
ConcreteInstr("LOAD_CONST", 2, lineno=4),
|
||||
|
|
@ -1340,11 +1381,9 @@ class BytecodeToConcreteTests(TestCase):
|
|||
#
|
||||
# Thus we need to make an additional pass. This test only verifies
|
||||
# case where 2 passes is insufficient but three is enough.
|
||||
|
||||
if not WORDCODE:
|
||||
# Could be done pre-WORDCODE, but that requires 2**16 bytes of
|
||||
# code.
|
||||
return
|
||||
#
|
||||
# On Python > 3.10 we need to double the number since the offset is now
|
||||
# in term of instructions and not bytes.
|
||||
|
||||
# Create code from comment above.
|
||||
code = Bytecode()
|
||||
|
|
@ -1353,11 +1392,17 @@ class BytecodeToConcreteTests(TestCase):
|
|||
nop = "NOP"
|
||||
code.append(Instr("JUMP_ABSOLUTE", label1))
|
||||
code.append(Instr("JUMP_ABSOLUTE", label2))
|
||||
for x in range(4, 254, 2):
|
||||
# Need 254 * 2 + 2 since the arg will change by 1 instruction rather than 2
|
||||
# bytes.
|
||||
for x in range(4, 510 if OFFSET_AS_INSTRUCTION else 254, 2):
|
||||
code.append(Instr(nop))
|
||||
code.append(label1)
|
||||
code.append(Instr(nop))
|
||||
for x in range(256, 300, 2):
|
||||
for x in range(
|
||||
514 if OFFSET_AS_INSTRUCTION else 256,
|
||||
600 if OFFSET_AS_INSTRUCTION else 300,
|
||||
2,
|
||||
):
|
||||
code.append(Instr(nop))
|
||||
code.append(label2)
|
||||
code.append(Instr(nop))
|
||||
|
|
@ -1381,18 +1426,20 @@ class BytecodeToConcreteTests(TestCase):
|
|||
instead generate a series of many jumps. Each pass of compute_jumps()
|
||||
extends one more instruction, which in turn causes the one behind it
|
||||
to be extended on the next pass.
|
||||
|
||||
"""
|
||||
if not WORDCODE:
|
||||
return
|
||||
|
||||
# N: the number of unextended instructions that can be squeezed into a
|
||||
# set of bytes adressable by the arg of an unextended instruction.
|
||||
# The answer is "128", but here's how we arrive at it (and it also
|
||||
# hints at how to make this work for pre-WORDCODE).
|
||||
# The answer is "128", but here's how we arrive at it.
|
||||
max_unextended_offset = 1 << 8
|
||||
unextended_branch_instr_size = 2
|
||||
N = max_unextended_offset // unextended_branch_instr_size
|
||||
|
||||
# When using instruction rather than bytes in the offset multiply by 2
|
||||
if OFFSET_AS_INSTRUCTION:
|
||||
N *= 2
|
||||
|
||||
nop = "UNARY_POSITIVE" # don't use NOP, dis.stack_effect will raise
|
||||
|
||||
# The number of jumps will be equal to the number of labels. The
|
||||
|
|
|
|||
|
|
@ -6,12 +6,14 @@ pytestmark = pytest.mark.skipif(not IS_PY36_OR_GREATER or not IS_CPYTHON or not
|
|||
#!/usr/bin/env python3
|
||||
import contextlib
|
||||
import io
|
||||
import sys
|
||||
import textwrap
|
||||
import unittest
|
||||
|
||||
from _pydevd_frame_eval.vendored import bytecode
|
||||
from _pydevd_frame_eval.vendored.bytecode import Label, Instr, Bytecode, BasicBlock, ControlFlowGraph
|
||||
from _pydevd_frame_eval.vendored.bytecode.tests import disassemble, WORDCODE
|
||||
from _pydevd_frame_eval.vendored.bytecode.concrete import OFFSET_AS_INSTRUCTION
|
||||
from _pydevd_frame_eval.vendored.bytecode.tests import disassemble
|
||||
|
||||
|
||||
class DumpCodeTests(unittest.TestCase):
|
||||
|
|
@ -39,10 +41,11 @@ class DumpCodeTests(unittest.TestCase):
|
|||
code = disassemble(source, function=True)
|
||||
|
||||
# without line numbers
|
||||
expected = """
|
||||
enum_repr = "<Compare.EQ: 2>"
|
||||
expected = f"""
|
||||
LOAD_FAST 'test'
|
||||
LOAD_CONST 1
|
||||
COMPARE_OP <Compare.EQ: 2>
|
||||
COMPARE_OP {enum_repr}
|
||||
POP_JUMP_IF_FALSE <label_instr6>
|
||||
LOAD_CONST 1
|
||||
RETURN_VALUE
|
||||
|
|
@ -50,7 +53,7 @@ class DumpCodeTests(unittest.TestCase):
|
|||
label_instr6:
|
||||
LOAD_FAST 'test'
|
||||
LOAD_CONST 2
|
||||
COMPARE_OP <Compare.EQ: 2>
|
||||
COMPARE_OP {enum_repr}
|
||||
POP_JUMP_IF_FALSE <label_instr13>
|
||||
LOAD_CONST 2
|
||||
RETURN_VALUE
|
||||
|
|
@ -67,10 +70,10 @@ label_instr13:
|
|||
self.check_dump_bytecode(code, expected)
|
||||
|
||||
# with line numbers
|
||||
expected = """
|
||||
expected = f"""
|
||||
L. 2 0: LOAD_FAST 'test'
|
||||
1: LOAD_CONST 1
|
||||
2: COMPARE_OP <Compare.EQ: 2>
|
||||
2: COMPARE_OP {enum_repr}
|
||||
3: POP_JUMP_IF_FALSE <label_instr6>
|
||||
L. 3 4: LOAD_CONST 1
|
||||
5: RETURN_VALUE
|
||||
|
|
@ -78,7 +81,7 @@ label_instr13:
|
|||
label_instr6:
|
||||
L. 4 7: LOAD_FAST 'test'
|
||||
8: LOAD_CONST 2
|
||||
9: COMPARE_OP <Compare.EQ: 2>
|
||||
9: COMPARE_OP {enum_repr}
|
||||
10: POP_JUMP_IF_FALSE <label_instr13>
|
||||
L. 5 11: LOAD_CONST 2
|
||||
12: RETURN_VALUE
|
||||
|
|
@ -128,12 +131,13 @@ label_instr13:
|
|||
code = ControlFlowGraph.from_bytecode(code)
|
||||
|
||||
# without line numbers
|
||||
enum_repr = "<Compare.EQ: 2>"
|
||||
expected = textwrap.dedent(
|
||||
"""
|
||||
f"""
|
||||
block1:
|
||||
LOAD_FAST 'test'
|
||||
LOAD_CONST 1
|
||||
COMPARE_OP <Compare.EQ: 2>
|
||||
COMPARE_OP {enum_repr}
|
||||
POP_JUMP_IF_FALSE <block3>
|
||||
-> block2
|
||||
|
||||
|
|
@ -144,7 +148,7 @@ label_instr13:
|
|||
block3:
|
||||
LOAD_FAST 'test'
|
||||
LOAD_CONST 2
|
||||
COMPARE_OP <Compare.EQ: 2>
|
||||
COMPARE_OP {enum_repr}
|
||||
POP_JUMP_IF_FALSE <block5>
|
||||
-> block4
|
||||
|
||||
|
|
@ -162,11 +166,11 @@ label_instr13:
|
|||
|
||||
# with line numbers
|
||||
expected = textwrap.dedent(
|
||||
"""
|
||||
f"""
|
||||
block1:
|
||||
L. 2 0: LOAD_FAST 'test'
|
||||
1: LOAD_CONST 1
|
||||
2: COMPARE_OP <Compare.EQ: 2>
|
||||
2: COMPARE_OP {enum_repr}
|
||||
3: POP_JUMP_IF_FALSE <block3>
|
||||
-> block2
|
||||
|
||||
|
|
@ -177,7 +181,7 @@ label_instr13:
|
|||
block3:
|
||||
L. 4 0: LOAD_FAST 'test'
|
||||
1: LOAD_CONST 2
|
||||
2: COMPARE_OP <Compare.EQ: 2>
|
||||
2: COMPARE_OP {enum_repr}
|
||||
3: POP_JUMP_IF_FALSE <block5>
|
||||
-> block4
|
||||
|
||||
|
|
@ -206,85 +210,45 @@ label_instr13:
|
|||
code = code.to_concrete_bytecode()
|
||||
|
||||
# without line numbers
|
||||
if WORDCODE:
|
||||
expected = """
|
||||
expected = f"""
|
||||
0 LOAD_FAST 0
|
||||
2 LOAD_CONST 1
|
||||
4 COMPARE_OP 2
|
||||
6 POP_JUMP_IF_FALSE 12
|
||||
6 POP_JUMP_IF_FALSE {6 if OFFSET_AS_INSTRUCTION else 12}
|
||||
8 LOAD_CONST 1
|
||||
10 RETURN_VALUE
|
||||
12 LOAD_FAST 0
|
||||
14 LOAD_CONST 2
|
||||
16 COMPARE_OP 2
|
||||
18 POP_JUMP_IF_FALSE 24
|
||||
18 POP_JUMP_IF_FALSE {12 if OFFSET_AS_INSTRUCTION else 24}
|
||||
20 LOAD_CONST 2
|
||||
22 RETURN_VALUE
|
||||
24 LOAD_CONST 3
|
||||
26 RETURN_VALUE
|
||||
""".lstrip(
|
||||
"\n"
|
||||
)
|
||||
else:
|
||||
expected = """
|
||||
0 LOAD_FAST 0
|
||||
3 LOAD_CONST 1
|
||||
6 COMPARE_OP 2
|
||||
9 POP_JUMP_IF_FALSE 16
|
||||
12 LOAD_CONST 1
|
||||
15 RETURN_VALUE
|
||||
16 LOAD_FAST 0
|
||||
19 LOAD_CONST 2
|
||||
22 COMPARE_OP 2
|
||||
25 POP_JUMP_IF_FALSE 32
|
||||
28 LOAD_CONST 2
|
||||
31 RETURN_VALUE
|
||||
32 LOAD_CONST 3
|
||||
35 RETURN_VALUE
|
||||
""".lstrip(
|
||||
"\n"
|
||||
)
|
||||
"\n"
|
||||
)
|
||||
self.check_dump_bytecode(code, expected)
|
||||
|
||||
# with line numbers
|
||||
if WORDCODE:
|
||||
expected = """
|
||||
expected = f"""
|
||||
L. 2 0: LOAD_FAST 0
|
||||
2: LOAD_CONST 1
|
||||
4: COMPARE_OP 2
|
||||
6: POP_JUMP_IF_FALSE 12
|
||||
6: POP_JUMP_IF_FALSE {6 if OFFSET_AS_INSTRUCTION else 12}
|
||||
L. 3 8: LOAD_CONST 1
|
||||
10: RETURN_VALUE
|
||||
L. 4 12: LOAD_FAST 0
|
||||
14: LOAD_CONST 2
|
||||
16: COMPARE_OP 2
|
||||
18: POP_JUMP_IF_FALSE 24
|
||||
18: POP_JUMP_IF_FALSE {12 if OFFSET_AS_INSTRUCTION else 24}
|
||||
L. 5 20: LOAD_CONST 2
|
||||
22: RETURN_VALUE
|
||||
L. 6 24: LOAD_CONST 3
|
||||
26: RETURN_VALUE
|
||||
""".lstrip(
|
||||
"\n"
|
||||
)
|
||||
else:
|
||||
expected = """
|
||||
L. 2 0: LOAD_FAST 0
|
||||
3: LOAD_CONST 1
|
||||
6: COMPARE_OP 2
|
||||
9: POP_JUMP_IF_FALSE 16
|
||||
L. 3 12: LOAD_CONST 1
|
||||
15: RETURN_VALUE
|
||||
L. 4 16: LOAD_FAST 0
|
||||
19: LOAD_CONST 2
|
||||
22: COMPARE_OP 2
|
||||
25: POP_JUMP_IF_FALSE 32
|
||||
L. 5 28: LOAD_CONST 2
|
||||
31: RETURN_VALUE
|
||||
L. 6 32: LOAD_CONST 3
|
||||
35: RETURN_VALUE
|
||||
""".lstrip(
|
||||
"\n"
|
||||
)
|
||||
"\n"
|
||||
)
|
||||
self.check_dump_bytecode(code, expected, lineno=True)
|
||||
|
||||
def test_type_validation(self):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import textwrap
|
||||
import types
|
||||
|
||||
|
||||
def get_code(source, *, filename="<string>", function=False):
|
||||
source = textwrap.dedent(source).strip()
|
||||
code = compile(source, filename, "exec")
|
||||
if function:
|
||||
sub_code = [
|
||||
const for const in code.co_consts if isinstance(const, types.CodeType)
|
||||
]
|
||||
if len(sub_code) != 1:
|
||||
raise ValueError("unable to find function code")
|
||||
code = sub_code[0]
|
||||
return code
|
||||
|
|
@ -92,7 +92,7 @@ def _start_monitoring_threads():
|
|||
os._exit(123)
|
||||
|
||||
dump_current_frames_thread = DumpThreads()
|
||||
dump_current_frames_thread.setDaemon(True) # Daemon so that this thread doesn't halt it!
|
||||
dump_current_frames_thread.daemon = True # Daemon so that this thread doesn't halt it!
|
||||
dump_current_frames_thread.start()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ if __name__ == '__main__':
|
|||
server_thread = threading.Thread(target=start_console_server,
|
||||
name='ServerThread',
|
||||
args=(host, int(port), interpreter))
|
||||
server_thread.setDaemon(True)
|
||||
server_thread.daemon = True
|
||||
server_thread.start()
|
||||
|
||||
sys.stdin = StdIn(interpreter, host, client_port, sys.stdin)
|
||||
|
|
|
|||
|
|
@ -318,5 +318,5 @@ if __name__ == '__main__':
|
|||
|
||||
|
||||
dump_current_frames_thread = DumpThreads()
|
||||
dump_current_frames_thread.setDaemon(True) # Daemon so that this thread doesn't halt it!
|
||||
dump_current_frames_thread.daemon = True # Daemon so that this thread doesn't halt it!
|
||||
dump_current_frames_thread.start()
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ class Test(unittest.TestCase):
|
|||
client_thread = ClientThread(client_port)
|
||||
client_thread.requested_input = False
|
||||
client_thread.notified_finished = 0
|
||||
client_thread.setDaemon(True)
|
||||
client_thread.daemon = True
|
||||
client_thread.start()
|
||||
return client_thread
|
||||
|
||||
|
|
@ -84,7 +84,7 @@ class Test(unittest.TestCase):
|
|||
print('Starting server with:', pydev_localhost.get_localhost(), self.server_port, self.client_port)
|
||||
pydevconsole.start_server(pydev_localhost.get_localhost(), self.server_port, self.client_port)
|
||||
server_thread = ServerThread(client_port, server_port)
|
||||
server_thread.setDaemon(True)
|
||||
server_thread.daemon = True
|
||||
server_thread.start()
|
||||
|
||||
client_thread = self.start_client_thread(client_port) #@UnusedVariable
|
||||
|
|
|
|||
|
|
@ -238,7 +238,7 @@ class TestRunningCode(TestBase):
|
|||
self.client_server.shutdown()
|
||||
|
||||
client_thread = ClientThread(client_port)
|
||||
client_thread.setDaemon(True)
|
||||
client_thread.daemon = True
|
||||
client_thread.start()
|
||||
return client_thread
|
||||
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ class Test(unittest.TestCase):
|
|||
client_thread = ClientThread(client_port)
|
||||
client_thread.requested_input = False
|
||||
client_thread.notified_finished = 0
|
||||
client_thread.setDaemon(True)
|
||||
client_thread.daemon = True
|
||||
client_thread.start()
|
||||
return client_thread
|
||||
|
||||
|
|
@ -185,7 +185,7 @@ class Test(unittest.TestCase):
|
|||
socket_code(socket)
|
||||
|
||||
debugger_thread = DebuggerServerThread(debugger_port, socket_code)
|
||||
debugger_thread.setDaemon(True)
|
||||
debugger_thread.daemon = True
|
||||
debugger_thread.start()
|
||||
return debugger_thread
|
||||
|
||||
|
|
@ -219,7 +219,7 @@ class Test(unittest.TestCase):
|
|||
pydevconsole.start_server(pydev_localhost.get_localhost(), self.server_port, self.client_port)
|
||||
|
||||
server_thread = ServerThread(client_port, server_port)
|
||||
server_thread.setDaemon(True)
|
||||
server_thread.daemon = True
|
||||
server_thread.start()
|
||||
|
||||
client_thread = self.start_client_thread(client_port) # @UnusedVariable
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ class ReaderThread(threading.Thread):
|
|||
except ImportError:
|
||||
from Queue import Queue
|
||||
|
||||
self.setDaemon(True)
|
||||
self.daemon = True
|
||||
self._buffer = b''
|
||||
self.sock = sock
|
||||
self._queue = Queue()
|
||||
|
|
@ -382,7 +382,7 @@ def read_process(stream, buffer, debug_stream, stream_name, finish):
|
|||
|
||||
def start_in_daemon_thread(target, args):
|
||||
t0 = threading.Thread(target=target, args=args)
|
||||
t0.setDaemon(True)
|
||||
t0.daemon = True
|
||||
t0.start()
|
||||
|
||||
|
||||
|
|
@ -671,7 +671,7 @@ class AbstractWriterThread(threading.Thread):
|
|||
def __init__(self, *args, **kwargs):
|
||||
threading.Thread.__init__(self, *args, **kwargs)
|
||||
self.process = None # Set after the process is created.
|
||||
self.setDaemon(True)
|
||||
self.daemon = True
|
||||
self.finished_ok = False
|
||||
self.finished_initialization = False
|
||||
self._next_breakpoint_id = 0
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
def Call():
|
||||
b = True
|
||||
while b: # expected
|
||||
pass # requested
|
||||
while b: # expected
|
||||
# requested
|
||||
pass # Note: until 3.10 a pass didn't generate a line event, but starting at 3.10, it does...
|
||||
break
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
Call()
|
||||
print('TEST SUCEEDED!')
|
||||
print('TEST SUCEEDED!')
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ def thread_func():
|
|||
|
||||
|
||||
th = threading.Thread(target=thread_func)
|
||||
th.setDaemon(True)
|
||||
th.daemon = True
|
||||
th.start()
|
||||
|
||||
event = threading.Event()
|
||||
|
|
|
|||
|
|
@ -9,6 +9,6 @@ async def run():
|
|||
print(p)
|
||||
|
||||
if __name__ == "__main__":
|
||||
loop = asyncio.get_event_loop()
|
||||
loop = asyncio.get_event_loop_policy().get_event_loop()
|
||||
loop.run_until_complete(run())
|
||||
print('TEST SUCEEDED')
|
||||
|
|
@ -35,7 +35,7 @@ def thread_func2(n):
|
|||
|
||||
|
||||
th = threading.Thread(target=lambda: thread_func2(1))
|
||||
th.setDaemon(True)
|
||||
th.daemon = True
|
||||
th.start()
|
||||
|
||||
th.join()
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ def thread_func2(n):
|
|||
|
||||
|
||||
th = threading.Thread(target=lambda: thread_func2(1))
|
||||
th.setDaemon(True)
|
||||
th.daemon = True
|
||||
th.start()
|
||||
|
||||
th.join()
|
||||
|
|
|
|||
|
|
@ -22,13 +22,16 @@ def check(found, expected):
|
|||
|
||||
last_offset = -1
|
||||
for f, e in zip(found, expected):
|
||||
if isinstance(e.name, (list, tuple, set)):
|
||||
assert f.name in e.name
|
||||
else:
|
||||
assert f.name == e.name
|
||||
assert f.is_visited == e.is_visited
|
||||
assert f.line == e.line
|
||||
assert f.call_order == e.call_order
|
||||
try:
|
||||
if isinstance(e.name, (list, tuple, set)):
|
||||
assert f.name in e.name
|
||||
else:
|
||||
assert f.name == e.name
|
||||
assert f.is_visited == e.is_visited
|
||||
assert f.line == e.line
|
||||
assert f.call_order == e.call_order
|
||||
except AssertionError as exc:
|
||||
raise AssertionError('%s\nError with: %s - %s' % (exc, f, e))
|
||||
|
||||
# We can't check the offset because it may be different among different python versions
|
||||
# so, just check that it's always in order.
|
||||
|
|
@ -1098,18 +1101,23 @@ def _test_find_bytecode():
|
|||
import glob
|
||||
import dis
|
||||
from io import StringIO
|
||||
root_dir = 'C:\\bin\\Miniconda\\envs\\py36_tests\\Lib\\site-packages\\'
|
||||
root_dir = 'C:\\bin\\Python310\\Lib\\site-packages\\'
|
||||
|
||||
i = 0
|
||||
for filename in glob.iglob(root_dir + '**/*.py', recursive=True):
|
||||
print(filename)
|
||||
with open(filename, 'r', encoding='utf-8') as stream:
|
||||
contents = stream.read()
|
||||
try:
|
||||
contents = stream.read()
|
||||
except:
|
||||
sys.stderr.write('Unable to read file: %s' % (filename,))
|
||||
continue
|
||||
|
||||
code_obj = compile(contents, filename, 'exec')
|
||||
s = StringIO()
|
||||
dis.dis(code_obj, file=s)
|
||||
if 'EXTENDED_ARG' in s.getvalue():
|
||||
# https://docs.python.org/3.10/library/dis.html has references to the new opcodes added.
|
||||
if 'COPY_DICT_WITHOUT_KEYS' in s.getvalue():
|
||||
dis.dis(code_obj)
|
||||
raise AssertionError('Found bytecode in: %s' % filename)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue