Comments (1)
Another example that shows a slightly different issue, that seems related to a bad reconstruction of the exception table (this requires urllib3
)
import bytecode as b
impoty urllib3
urllib3.connectionpool.HTTPConnectionPool.urlopen.__code__ = b.Bytecode.from_code(
urllib3.connectionpool.HTTPConnectionPool.urlopen.__code__
).to_code()
b.Bytecode.from_code(urllib3.connectionpool.HTTPConnectionPool.urlopen.__code__)
Result:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
ddtrace/contrib/urllib3/patch.py:212: in patch
b.Bytecode.from_code(urllib3.connectionpool.HTTPConnectionPool.urlopen.__code__)
.riot/venv_py3114/lib/python3.11/site-packages/bytecode/bytecode.py:283: in from_code
return concrete.to_bytecode(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <ConcreteBytecode instr#=1454>, prune_caches = True
conserve_exception_block_stackdepth = False
def to_bytecode(
self,
prune_caches: bool = True,
conserve_exception_block_stackdepth: bool = False,
) -> _bytecode.Bytecode:
# On 3.11 we generate pseudo-instruction from the exception table
# Copy instruction and remove extended args if any (in-place)
c_instructions = self[:]
self._remove_extended_args(c_instructions)
# Find jump targets
jump_targets: Set[int] = set()
offset = 0
for c_instr in c_instructions:
if isinstance(c_instr, SetLineno):
continue
target = c_instr.get_jump_target(offset)
if target is not None:
jump_targets.add(target)
offset += (c_instr.size // 2) if OFFSET_AS_INSTRUCTION else c_instr.size
# On 3.11+ we need to also look at the exception table for jump targets
for ex_entry in self.exception_table:
jump_targets.add(ex_entry.target)
# Create look up dict to find entries based on either exception handling
# block exit or entry offsets. Several blocks can end on the same instruction
# so we store a list of entry per offset.
ex_start: Dict[int, ExceptionTableEntry] = {}
ex_end: Dict[int, List[ExceptionTableEntry]] = {}
for entry in self.exception_table:
# Ensure we do not have more than one entry with identical starting
# offsets
assert entry.start_offset not in ex_start
ex_start[entry.start_offset] = entry
ex_end.setdefault(entry.stop_offset, []).append(entry)
# Create labels and instructions
jumps: List[Tuple[int, int]] = []
instructions: List[Union[Instr, Label, TryBegin, TryEnd, SetLineno]] = []
labels = {}
tb_instrs: Dict[ExceptionTableEntry, TryBegin] = {}
offset = 0
# In Python 3.11+ cell and varnames can be shared and are indexed in a single
# array.
# As a consequence, the instruction argument can be either:
# - < len(varnames): the name is shared an we can directly use
# the index to access the name in cellvars
# - > len(varnames): the name is not shared and is offset by the
# number unshared varname.
# Free vars are never shared and correspond to index larger than the
# largest cell var.
# See PyCode_NewWithPosOnlyArgs
if sys.version_info >= (3, 11):
cells_lookup = self.varnames + [
n for n in self.cellvars if n not in self.varnames
]
ncells = len(cells_lookup)
else:
ncells = len(self.cellvars)
cells_lookup = self.cellvars
for lineno, c_instr in self._normalize_lineno(
c_instructions, self.first_lineno
):
if offset in jump_targets:
label = Label()
labels[offset] = label
instructions.append(label)
# Handle TryBegin pseudo instructions
if offset in ex_start:
entry = ex_start[offset]
tb_instr = TryBegin(
Label(),
entry.push_lasti,
entry.stack_depth if conserve_exception_block_stackdepth else UNSET,
)
# Per entry store the pseudo instruction associated
tb_instrs[entry] = tb_instr
instructions.append(tb_instr)
jump_target = c_instr.get_jump_target(offset)
size = c_instr.size
# If an instruction uses extended args, those appear before the instruction
# causing the instruction to appear at offset that accounts for extended
# args. So we first update the offset to account for extended args, then
# record the instruction offset and then add the instruction itself to the
# offset.
offset += (size // 2 - 1) if OFFSET_AS_INSTRUCTION else (size - 2)
current_instr_offset = offset
offset += 1 if OFFSET_AS_INSTRUCTION else 2
# on Python 3.11+ remove CACHE opcodes if we are requested to do so.
# We are careful to first advance the offset and check that the CACHE
# is not a jump target. It should never be the case but we double check.
if prune_caches and c_instr.name == "CACHE":
assert jump_target is None
# We may need to insert a TryEnd after a CACHE so we need to run the
# through the last block.
else:
arg: InstrArg
c_arg = c_instr.arg
# FIXME: better error reporting
if c_instr.opcode in _opcode.hasconst:
arg = self.consts[c_arg]
elif c_instr.opcode in _opcode.haslocal:
arg = self.varnames[c_arg]
elif c_instr.opcode in _opcode.hasname:
if c_instr.name in BITFLAG_INSTRUCTIONS:
arg = (bool(c_arg & 1), self.names[c_arg >> 1])
elif c_instr.name in BITFLAG2_INSTRUCTIONS:
arg = (bool(c_arg & 1), bool(c_arg & 2), self.names[c_arg >> 2])
else:
arg = self.names[c_arg]
elif c_instr.opcode in _opcode.hasfree:
if c_arg < ncells:
name = cells_lookup[c_arg]
arg = CellVar(name)
else:
name = self.freevars[c_arg - ncells]
arg = FreeVar(name)
elif c_instr.opcode in _opcode.hascompare:
arg = Compare(
(c_arg >> 4) if sys.version_info >= (3, 12) else c_arg
)
elif c_instr.opcode in INTRINSIC_1OP:
arg = Intrinsic1Op(c_arg)
elif c_instr.opcode in INTRINSIC_2OP:
arg = Intrinsic2Op(c_arg)
else:
arg = c_arg
location = c_instr.location or InstrLocation(lineno, None, None, None)
if jump_target is not None:
arg = PLACEHOLDER_LABEL
instr_index = len(instructions)
jumps.append((instr_index, jump_target))
instructions.append(Instr(c_instr.name, arg, location=location))
# We now insert the TryEnd entries
if current_instr_offset in ex_end:
entries = ex_end[current_instr_offset]
for entry in reversed(entries):
> instructions.append(TryEnd(tb_instrs[entry]))
E KeyError: ExceptionTableEntry(start_offset=497, stop_offset=497, target=908, stack_depth=0, push_lasti=False
Looking at the bytecode around the (doubled) offset, there seems to be an EXTENDED_ARG
that is skipped. The original table covers
992 EXTENDED_ARG 1
994 JUMP_FORWARD 360 (to 1716)
whilst the new table only covers the branching instruction
994 JUMP_FORWARD 360 (to 1716)
from bytecode.
Related Issues (20)
- Missing tag on 0.12.0 release HOT 1
- Update jump address handling for 3.10 HOT 1
- Bytecode doesn't properly set linenumbers (in corner case in Python 3.10rc1) HOT 2
- Errors in compute stacksize on 3.10 HOT 7
- Please clarify the copyright HOT 4
- Remove `Compare.EXC_MATCH` on Python>=3.9 HOT 1
- Update pre_and_post_stack_effect HOT 1
- Labels don't seem to be handled correctly with Python<3.9 HOT 5
- The `Compare` enum is broken starting from Python 3.9
- Python 3.11 support HOT 5
- EXTENDED_ARG + NOP Error HOT 3
- except Exception as e fails on Python 3.11 HOT 1
- read .pyc HOT 3
- Possible "hysteresis" in bytecode recompilation with 3.12 HOT 7
- `ValueError: ('TryBegin target must a BasicBlock, got %s', 'Label')` when using `Label` with `TryBegin` HOT 14
- `LOAD_METHOD` is still a valid opname but doesn't seem to be fully supported HOT 1
- `_encode_varint` cannot handle 0 HOT 1
- Running pytest segfaults with Python 3.12 on armhf HOT 11
- Can't call function with no arguments (py312) HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from bytecode.