The def/lambda signature printed positional, keyword-only(*), *args, **kwargs,
producing invalid 'def f(*, kw, *args)'. Python order is positional, *args,
keyword-only, **kwargs. Index locals explicitly (they are stored positional,
keyword-only, *args, **kwargs) and emit in source order.
Harness: 0 gate change (affected files have other errors) but fixes incorrect
signatures across many files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A finally compiles to: try body -> finally body (normal copy) -> JUMP over an
exception handler that duplicates the finally body and re-raises. A pre-pass
(ScanTryFinally) recognizes this from the exception table, distinguishing
finally from bare/typed except by the handler shape after PUSH_EXC_INFO (no
POP_TOP, no CHECK_EXC_MATCH). The try-body entry opens a CONTAINER carrying the
finally end + BLK_TRY ending at the real body end; the try close opens a
BLK_FINALLY for the normal copy; the duplicate exception handler region is
skipped.
Harness: +2, 0 regressions (corpus 20->22, decompilation target 226).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3.11 'with' compiles to: body -> implicit __exit__(None,None,None) -> JUMP over
an exception-cleanup handler -> resume. A pre-pass (ScanWithBlocks) recognizes
this shape from the exception table: it records the body end and the resume
offset, verifying the handler begins with PUSH_EXC_INFO; WITH_EXCEPT_START and
that the normal-exit jump skips over it. BEFORE_WITH then opens an ASTWithBlock
(the context manager stays on the stack for the STORE/POP_TOP -> expr + 'as'),
and the [bodyEnd, resume) cleanup region is skipped during decompilation.
With-statements without this clean shape are left unhandled (no regression).
Harness: +2, 0 regressions (corpus 19->20, decompilation target: 225→226).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- PUSH_EXC_INFO pushes an exception sentinel; CHECK_EXC_MATCH keeps it so the
'as <var>' STORE can bind it; POP_TOP discards it for bare handlers.
- Emit 'except <type> as <var>:' and suppress the compiler cleanup
(<var> = None; del <var>).
- WITH_EXCEPT_START no longer aliases SETUP_WITH; it consumes the sentinel so
the with-cleanup never leaks it.
- Detect <setcomp>/<dictcomp> (not just <listcomp>) as comprehensions so
SET_ADD/MAP_ADD reconstruct them for inlining.
Harness: +1, 0 regressions (foundation for multi-except/finally/with).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
NODE_RAISE always joined params with commas (Python 2 syntax). For Python 3,
two params is 'raise X from Y'. Harness: +1, 0 regressions; also fixes the
common 'raise X from None' idiom across many files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SWAP_A was modelled only as tuple-unpack construction, corrupting the stack
for the 3.11 chained-comparison idiom (SWAP n; COPY n). Implement it as a
genuine stack swap via FastStack::swap.
Harness: +17 files (decompilation target: 212→224, stdlib corpus 13->18), 0 regressions.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Modify .gitignore
* Added support for SWAP and WITH_EXCEPT_START, WITH_EXCEPT_START is simply added on top of SETUP_WITH_A so that it works properly.
* Resolve the warning about comparing size_t and int.
* Revert "Resolve the warning about comparing size_t and int."
This reverts commit 54dfe36629.
* Reapply "Resolve the warning about comparing size_t and int."
This reverts commit d21d1681ed.
* Modify decompyle_test.sh
* Modify .gitignore
* Fix the logic error by placing the assignment inside the tuple
* Re-adding test files
* Fixing redundant brackets
* Add support for swap bytecode and simple WITH_EXCEPT_START bytecode support.
* Clean up some formatting issues
---------
Co-authored-by: Michael Hansen <zrax0111@gmail.com>
The opcode itself is exactly the same as `LOAD_DEREF`
1) The problem is when the class is a closure (e.g. defined inside a function body) then there is a `BUILD_TUPLE` after the `LOAD_BUILD_CLASS` which makes problems.
2) There is another problem which makes the `code->name()` of the class to be part of the function locals. (e.g. `func.<locals>.my_class` instead of `my_class`) which makes the check `srcString->isEqual(code->name().cast<PycObject>())` be invalid.
* fix#396 Unsupported Node type: 27
* Add test file
* Modify a little comment
* Modify the test pyc file name
* Delete redundant pyc files
* retest
* Add newline at EOF
---------
Co-authored-by: Michael Hansen <zrax0111@gmail.com>
* Apparently should be enough ?
* Add `GEN_START` test
* Add `GEN_START` test compiled
* Add `GEN_START` test tokenized
* Smaller test since only POP is needed.
* Smaller test since only POP is needed.
* Smaller test since only POP is needed.
* dos2unix