You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-03 08:49:51 +08:00
Compare commits
130 Commits
release-py
...
release-py
Author | SHA1 | Date | |
---|---|---|---|
|
6055c5e165 | ||
|
e0ed187ea6 | ||
|
eafe048c7e | ||
|
c0e553dbb5 | ||
|
35e4e03468 | ||
|
d1917046f4 | ||
|
55f12e36b7 | ||
|
81669ad7e7 | ||
|
5b9f9319a8 | ||
|
4b0892bcb5 | ||
|
74731a9d42 | ||
|
b9dfba7400 | ||
|
9ec43de039 | ||
|
5d42fe39bb | ||
|
e9b60ddbf0 | ||
|
0e04b12ad4 | ||
|
cb2b6d9bf4 | ||
|
a28f5604ce | ||
|
55ced53ca9 | ||
|
41f5835fcf | ||
|
70b77025ac | ||
|
918d4f5808 | ||
|
024f295feb | ||
|
0bb793239b | ||
|
f82165aaa7 | ||
|
4c77170ddf | ||
|
3e4889bcd7 | ||
|
7beac3f646 | ||
|
6b6755d599 | ||
|
4a904951f4 | ||
|
124267849c | ||
|
6bffae91fa | ||
|
da6e32b08e | ||
|
9379922c89 | ||
|
6dbdaedf7a | ||
|
dea17cd7f1 | ||
|
4f0a668b7c | ||
|
6746e5167d | ||
|
b32823bb7d | ||
|
54332ddffb | ||
|
b83d6c64ed | ||
|
95268cb14e | ||
|
5df09540b5 | ||
|
5e7632c33e | ||
|
1761ba2581 | ||
|
03d1c48088 | ||
|
9dd881fae1 | ||
|
2fc3886693 | ||
|
0dfbb27af5 | ||
|
7e59987af7 | ||
|
e42e3cc230 | ||
|
0560c32093 | ||
|
3f309cebab | ||
|
1f012f7c46 | ||
|
d3a42ff992 | ||
|
d1a3d42ab8 | ||
|
05fd992c48 | ||
|
47f1d888eb | ||
|
b1e650a7bd | ||
|
491572ed2d | ||
|
717b22bd13 | ||
|
ca9c227837 | ||
|
5e1d91cb94 | ||
|
e0def48020 | ||
|
5df384bb71 | ||
|
9a2534556c | ||
|
e80b36347a | ||
|
85269dc4d8 | ||
|
01a39bf8ed | ||
|
97999c5e67 | ||
|
9e37495493 | ||
|
77b93c5f21 | ||
|
0b198ee881 | ||
|
9e0c65881d | ||
|
c796d6a799 | ||
|
4563a547bc | ||
|
9cfd7d669e | ||
|
413f5aa5a5 | ||
|
b4426931ef | ||
|
3892fb533a | ||
|
92f5981661 | ||
|
54fe07e989 | ||
|
adc9b99106 | ||
|
1392b18bd7 | ||
|
2ea7487ca7 | ||
|
d4f6cec3d0 | ||
|
9ae84092cb | ||
|
85d68a7926 | ||
|
b3359439f9 | ||
|
b1705e283d | ||
|
9be9abc682 | ||
|
c17ac696d6 | ||
|
9e2119f1a9 | ||
|
eee751e22a | ||
|
86305097d2 | ||
|
2b0fefb95f | ||
|
c8d15e7654 | ||
|
1d7a3c6444 | ||
|
e7778f83f2 | ||
|
b51039ac1e | ||
|
1a627ba207 | ||
|
f73f0ba41c | ||
|
114f979555 | ||
|
ea75bcf47e | ||
|
6c6dcab857 | ||
|
0654aed6c8 | ||
|
7b38d2f1f8 | ||
|
dfbd60231b | ||
|
8b67f2ccd0 | ||
|
3447ca0767 | ||
|
aadea7224d | ||
|
1e858efafd | ||
|
da7421da1c | ||
|
ce88a72ea1 | ||
|
96ca68a6fe | ||
|
147b6e1cfe | ||
|
7725b8e7de | ||
|
d7b12f4da1 | ||
|
62ddbe320d | ||
|
c7b9e54e59 | ||
|
a694601264 | ||
|
3003070acb | ||
|
19d6dedcf5 | ||
|
51ad3fb36e | ||
|
f017acce21 | ||
|
5bef5683e4 | ||
|
4e1467adc8 | ||
|
7cdf0abb43 | ||
|
9b336251a7 | ||
|
7844456e1e |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -17,4 +17,5 @@
|
||||
__pycache__
|
||||
build
|
||||
/.venv*
|
||||
/.idea
|
||||
/.idea
|
||||
/.hypothesis
|
||||
|
648
ChangeLog
648
ChangeLog
@@ -1,3 +1,651 @@
|
||||
2017-11-13 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/main.py, uncompyle6/parser.py,
|
||||
uncompyle6/semantics/pysource.py: 2.4isms... Need print without parens. Handle old-style classes more properly?
|
||||
|
||||
2017-11-13 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/scanners/scanner3.py,
|
||||
uncompyle6/semantics/make_function.py: Get ready for release
|
||||
python-2.4-2.13.3
|
||||
|
||||
2017-11-13 rocky <rb@dustyfeet.com>
|
||||
|
||||
* : commit 35e4e034686065609b8b2e55cd37c9a391e16c00 Author: rocky
|
||||
<rb@dustyfeet.com> Date: Mon Nov 13 09:53:10 2017 -0500
|
||||
|
||||
2017-11-13 rocky <rb@dustyfeet.com>
|
||||
|
||||
* ChangeLog, NEWS, uncompyle6/version.py: Get ready for release
|
||||
2.13.3
|
||||
|
||||
2017-11-12 rocky <rb@dustyfeet.com>
|
||||
|
||||
* test/Makefile: Back off --verify for --weak-verify
|
||||
|
||||
2017-11-12 rocky <rb@dustyfeet.com>
|
||||
|
||||
* test/Makefile: Back off --verify for --weak-verify
|
||||
|
||||
2017-11-12 rocky <rb@dustyfeet.com>
|
||||
|
||||
* : Reinstate previously failed tests 2.6, 3.5 and 3.6 decompilation has gotten better
|
||||
|
||||
2017-11-10 rocky <rb@dustyfeet.com>
|
||||
|
||||
* __pkginfo__.py: Use newer xdis
|
||||
|
||||
2017-11-09 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/parsers/parse36.py, uncompyle6/semantics/pysource.py:
|
||||
Fix bug in return-optimized try stmt
|
||||
|
||||
2017-11-09 rocky <rb@dustyfeet.com>
|
||||
|
||||
* HOW-TO-REPORT-A-BUG.md: More detail is needed in bug reporting... sigh.
|
||||
|
||||
2017-11-09 rocky <rb@dustyfeet.com>
|
||||
|
||||
* test/simple_source/bug35/04_importlist.py, uncompyle6/parser.py,
|
||||
uncompyle6/semantics/consts.py: bug in 3.x importlists consts.py: add rule for importlists. imports weren't separated by ',
|
||||
'. parser.py: Make importlist a list type of node. test/* add test for importlist
|
||||
|
||||
2017-11-08 rocky <rb@dustyfeet.com>
|
||||
|
||||
* : commit e9b60ddbf020ee7f14d8d77c6f4a8588d2968377 Author: rocky
|
||||
<rb@dustyfeet.com> Date: Wed Nov 8 23:05:01 2017 -0500
|
||||
|
||||
2017-11-08 rocky <rb@dustyfeet.com>
|
||||
|
||||
* HOW-TO-REPORT-A-BUG.md: more wordsmithing
|
||||
|
||||
2017-11-08 rocky <rb@dustyfeet.com>
|
||||
|
||||
* HOW-TO-REPORT-A-BUG.md: more wordsmithing
|
||||
|
||||
2017-11-08 rocky <rb@dustyfeet.com>
|
||||
|
||||
* HOW-TO-REPORT-A-BUG.md: more wordsmithing
|
||||
|
||||
2017-11-08 rocky <rb@dustyfeet.com>
|
||||
|
||||
* HOW-TO-REPORT-A-BUG.md: Typo
|
||||
|
||||
2017-11-08 rocky <rb@dustyfeet.com>
|
||||
|
||||
* HOW-TO-REPORT-A-BUG.md: Typo
|
||||
|
||||
2017-11-08 rocky <rb@dustyfeet.com>
|
||||
|
||||
* HOW-TO-REPORT-A-BUG.md: Typo
|
||||
|
||||
2017-11-08 rocky <rb@dustyfeet.com>
|
||||
|
||||
* HOW-TO-REPORT-A-BUG.md: Typo
|
||||
|
||||
2017-11-08 rocky <rb@dustyfeet.com>
|
||||
|
||||
* HOW-TO-REPORT-A-BUG.md: Tweak how to report a bug.
|
||||
|
||||
2017-11-08 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/parser.py, uncompyle6/parsers/parse36.py,
|
||||
uncompyle6/scanners/scanner3.py: Add 3.6+ grammar for except's
|
||||
ending in RETURN... Not totally out of the maze in 3.6 control flow... There are still
|
||||
problems with erroneous RETURN_VALUEs becoming RETURN_END_IF,
|
||||
|
||||
2017-11-07 R. Bernstein <rocky@users.noreply.github.com>
|
||||
|
||||
* : Merge pull request #135 from rocky/3.6-instruction-refactor 3.6 instruction refactor
|
||||
|
||||
2017-11-06 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/scanners/scanner3.py: Small tweaks to sync up better
|
||||
with scanner2.py
|
||||
|
||||
2017-11-06 rocky <rb@dustyfeet.com>
|
||||
|
||||
* pytest/test_fjt.py: Remove parts of erroneous 2.7 test for now
|
||||
|
||||
2017-11-06 rocky <rb@dustyfeet.com>
|
||||
|
||||
* pytest/test_fjt.py, uncompyle6/scanners/scanner3.py,
|
||||
uncompyle6/scanners/scanner36.py: Fix 3.{3,4} pytest. Remove dup
|
||||
find_jump_targets
|
||||
|
||||
2017-11-06 rocky <rb@dustyfeet.com>
|
||||
|
||||
* Makefile, uncompyle6/scanners/scanner3.py,
|
||||
uncompyle6/scanners/scanner36.py: Move refactored find-jump-targets
|
||||
from 3.6 to 3.x
|
||||
|
||||
2017-11-06 rocky <rb@dustyfeet.com>
|
||||
|
||||
* test/Makefile, uncompyle6/scanners/scanner3.py,
|
||||
uncompyle6/scanners/scanner36.py: Move refactored ingest from 3.6 to
|
||||
3.x... We are getting away from working with bytecode in favor of working
|
||||
with full-fledged structured instructions Up next: find_jump_targets()
|
||||
|
||||
2017-11-06 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/parsers/parse36.py: awith custom COME_FROMs ... Now that jump branching has been properly fixed up for EXTENDED_ARG
|
||||
instructions which are more prevalent with wordcode encoding.
|
||||
|
||||
2017-11-06 rocky <rb@dustyfeet.com>
|
||||
|
||||
* : commit 9379922c89573972aa387e4f0b9abcba7358d1a3 Author: rocky
|
||||
<rb@dustyfeet.com> Date: Mon Nov 6 00:38:22 2017 -0500
|
||||
|
||||
2017-11-06 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/scanners/scanner36.py: Revert change that should have
|
||||
been in a branch
|
||||
|
||||
2017-11-06 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/scanners/scanner2.py,
|
||||
uncompyle6/scanners/scanner26.py, uncompyle6/scanners/scanner3.py,
|
||||
uncompyle6/scanners/scanner36.py: xdis _disassemble->disassemble
|
||||
|
||||
2017-11-04 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/semantics/fragments.py,
|
||||
uncompyle6/semantics/make_function.py,
|
||||
uncompyle6/semantics/pysource.py: Add flag to tolerate deparse
|
||||
errors... and keep going. The fragment parser should ignore errors in nested
|
||||
function definitions
|
||||
|
||||
2017-11-04 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/scanner.py, uncompyle6/semantics/fragments.py: Add
|
||||
Python 3.6.3 scanner lookup
|
||||
|
||||
2017-11-03 R. Bernstein <rocky@users.noreply.github.com>
|
||||
|
||||
* : Merge pull request #134 from mikemrm/master Corrected python3 import from queue
|
||||
|
||||
2017-10-29 rocky <rb@dustyfeet.com>
|
||||
|
||||
* test/simple_source/bug36/10_extended_arg_loop.py,
|
||||
uncompyle6/parsers/parse36.py, uncompyle6/scanners/scanner3.py:
|
||||
Python 3.6 control flow bug... Much more is needed, but it's a start
|
||||
|
||||
2017-10-29 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/verify.py: In verify, JUMP_BACK is the same as
|
||||
CONTINUE... at least for now. See FIXME in verify
|
||||
|
||||
2017-10-29 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/scanner.py, uncompyle6/scanners/scanner2.py,
|
||||
uncompyle6/scanners/scanner3.py, uncompyle6/scanners/scanner30.py:
|
||||
Python 3.6-inspired instruction size cleanup Revise and generalize for Python 3.6+ instructions vs < 3.6
|
||||
instuctions. Used more of the generalized methods in xdis and
|
||||
remove some (but not all) of the magic numbers. This is a lot of changes, but not all of the refactoring needed.
|
||||
Much crap still remains. Also, there are still bugs in handling 3.6
|
||||
bytecodes.
|
||||
|
||||
2017-10-24 rocky <rb@dustyfeet.com>
|
||||
|
||||
* Makefile, __pkginfo__.py: Bump uncompyle. Pypy 5.8.0-beta
|
||||
tolerance
|
||||
|
||||
2017-10-13 rocky <rb@dustyfeet.com>
|
||||
|
||||
* test/Makefile, uncompyle6/semantics/consts.py: Tag more semantic
|
||||
actions with nonterminals
|
||||
|
||||
2017-10-13 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/parser.py, uncompyle6/semantics/consts.py: More node
|
||||
checking in tables
|
||||
|
||||
2017-10-13 rocky <rb@dustyfeet.com>
|
||||
|
||||
* pytest/test_pysource.py, uncompyle6/parser.py,
|
||||
uncompyle6/parsers/parse24.py, uncompyle6/semantics/consts.py,
|
||||
uncompyle6/semantics/fragments.py, uncompyle6/semantics/pysource.py:
|
||||
Start allowing node names in template engine These are now used to assert we have the right node type. Simplify import_from
|
||||
|
||||
2017-10-13 rocky <rb@dustyfeet.com>
|
||||
|
||||
* HISTORY.md, uncompyle6/semantics/pysource.py: Small changes
|
||||
|
||||
2017-10-12 rocky <rb@dustyfeet.com>
|
||||
|
||||
* admin-tools/make-dist-newer.sh, admin-tools/make-dist-older.sh:
|
||||
Administrivia - generalize shell code
|
||||
|
||||
2017-10-12 rocky <rb@dustyfeet.com>
|
||||
|
||||
* : commit e42e3cc230237a448f48d10f3a6e11c8cda5e9c7 Author: rocky
|
||||
<rb@dustyfeet.com> Date: Thu Oct 12 07:29:52 2017 -0400
|
||||
|
||||
2017-10-12 rocky <rb@dustyfeet.com>
|
||||
|
||||
* admin-tools/how-to-make-a-release.md: Update instructions
|
||||
|
||||
2017-10-12 rocky <rb@dustyfeet.com>
|
||||
|
||||
* admin-tools/make-dist-newer.sh, admin-tools/make-dist-older.sh:
|
||||
Administrivia
|
||||
|
||||
2017-10-12 rocky <rb@dustyfeet.com>
|
||||
|
||||
* admin-tools/make-dist-newer.sh, admin-tools/make-dist-older.sh:
|
||||
Merge conflicts
|
||||
|
||||
2017-10-12 rocky <rb@dustyfeet.com>
|
||||
|
||||
* admin-tools/how-to-make-a-release.md: Minor
|
||||
|
||||
2017-10-12 rocky <rb@dustyfeet.com>
|
||||
|
||||
* admin-tools/how-to-make-a-release.md,
|
||||
admin-tools/pyenv-newer-versions, admin-tools/update-sources.sh:
|
||||
Sync
|
||||
|
||||
2017-10-12 rocky <rb@dustyfeet.com>
|
||||
|
||||
* NEWS, uncompyle6/parser.py, uncompyle6/scanners/tok.py,
|
||||
uncompyle6/semantics/consts.py,
|
||||
uncompyle6/semantics/make_function.py: Update news
|
||||
|
||||
2017-10-12 rocky <rb@dustyfeet.com>
|
||||
|
||||
* : commit b1e650a7bd79d58181557c95bc7ad64e1b8387e6 Merge: 491572e
|
||||
717b22b Author: rocky <rb@dustyfeet.com> Date: Thu Oct 12 06:52:24
|
||||
2017 -0400
|
||||
|
||||
2017-10-12 rocky <rb@dustyfeet.com>
|
||||
|
||||
* ChangeLog, Makefile, NEWS, admin-tools/how-to-make-a-release.txt,
|
||||
uncompyle6/version.py: Get ready for release 2.13.2
|
||||
|
||||
2017-10-12 rocky <rb@dustyfeet.com>
|
||||
|
||||
* ChangeLog, Makefile, NEWS, admin-tools/how-to-make-a-release.txt,
|
||||
uncompyle6/version.py: Get ready for release 2.13.2
|
||||
|
||||
2017-10-11 rocky <rb@dustyfeet.com>
|
||||
|
||||
* admin-tools/check-newer-versions.sh,
|
||||
admin-tools/check-older-versions.sh,
|
||||
admin-tools/how-to-make-a-release.txt,
|
||||
admin-tools/make-dist-newer.sh, admin-tools/make-dist-older.sh,
|
||||
admin-tools/pyenv-newer-versions, admin-tools/pyenv-older-versions,
|
||||
uncompyle6/parsers/parse37.py, uncompyle6/scanners/scanner37.py,
|
||||
uncompyle6/version.py: More administrivia
|
||||
|
||||
2017-10-11 rocky <rb@dustyfeet.com>
|
||||
|
||||
* admin-tools/check-older-versions.sh,
|
||||
admin-tools/make-dist-newer.sh, admin-tools/make-dist-older.sh,
|
||||
admin-tools/pyenv-newer-versions, admin-tools/pyenv-older-versions,
|
||||
admin-tools/setup-master.sh: Administrivia
|
||||
|
||||
2017-10-11 rocky <rb@dustyfeet.com>
|
||||
|
||||
* admin-tools/check-newer-versions.sh,
|
||||
admin-tools/check-older-versions.sh,
|
||||
admin-tools/how-to-make-a-release.txt,
|
||||
admin-tools/make-dist-newer.sh, admin-tools/make-dist-older.sh:
|
||||
Adminstrivia
|
||||
|
||||
2017-10-11 rocky <rb@dustyfeet.com>
|
||||
|
||||
* admin-tools/README.md, admin-tools/check-newer-versions.sh,
|
||||
admin-tools/check-older-versions.sh,
|
||||
admin-tools/how-to-make-a-release.txt,
|
||||
admin-tools/make-dist-newer.sh, admin-tools/make-dist-older.sh,
|
||||
admin-tools/pyenv-newer-versions, admin-tools/pyenv-older-versions,
|
||||
admin-tools/setup-master.sh, admin-tools/setup-python-2.4.sh: Some
|
||||
admin tools I use
|
||||
|
||||
2017-10-11 rocky <rb@dustyfeet.com>
|
||||
|
||||
* admin-tools/README.md, admin-tools/check-newer-versions.sh,
|
||||
admin-tools/check-older-versions.sh,
|
||||
admin-tools/how-to-make-a-release.txt,
|
||||
admin-tools/make-dist-newer.sh, admin-tools/make-dist-older.sh,
|
||||
admin-tools/pyenv-newer-versions, admin-tools/pyenv-older-versions,
|
||||
admin-tools/setup-master.sh, admin-tools/setup-python-2.4.sh: Some
|
||||
admin tools I use.
|
||||
|
||||
2017-10-11 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/parser.py: Remove creaping Python 2.6ism
|
||||
|
||||
2017-10-11 rocky <rb@dustyfeet.com>
|
||||
|
||||
* setup.py: remove python_requires
|
||||
|
||||
2017-10-11 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/bin/uncompile.py: Program name was incorrect. uncompile -> uncompyle6
|
||||
|
||||
2017-10-11 rocky <rb@dustyfeet.com>
|
||||
|
||||
* Makefile, NEWS, setup.py, uncompyle6/version.py: Administrivia
|
||||
woes
|
||||
|
||||
2017-10-10 rocky <rb@dustyfeet.com>
|
||||
|
||||
* pytest/test_grammar.py: Sync with master
|
||||
|
||||
2017-10-10 rocky <rb@dustyfeet.com>
|
||||
|
||||
* pytest/test_grammar.py, uncompyle6/parsers/parse26.py,
|
||||
uncompyle6/parsers/parse27.py, uncompyle6/parsers/parse34.py,
|
||||
uncompyle6/parsers/parse35.py, uncompyle6/parsers/parse36.py,
|
||||
uncompyle6/parsers/parse37.py: Sync with master
|
||||
|
||||
2017-10-10 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/semantics/fragments.py: Sync with master
|
||||
|
||||
2017-10-10 rocky <rb@dustyfeet.com>
|
||||
|
||||
* NEWS, __pkginfo__.py, pytest/test_grammar.py,
|
||||
uncompyle6/parser.py, uncompyle6/parsers/parse15.py,
|
||||
uncompyle6/parsers/parse2.py, uncompyle6/parsers/parse21.py,
|
||||
uncompyle6/parsers/parse22.py, uncompyle6/parsers/parse23.py,
|
||||
uncompyle6/parsers/parse24.py, uncompyle6/parsers/parse25.py,
|
||||
uncompyle6/parsers/parse26.py, uncompyle6/parsers/parse27.py,
|
||||
uncompyle6/parsers/parse3.py, uncompyle6/parsers/parse32.py,
|
||||
uncompyle6/parsers/parse34.py, uncompyle6/parsers/parse35.py,
|
||||
uncompyle6/parsers/parse36.py, uncompyle6/parsers/parse37.py,
|
||||
uncompyle6/scanners/scanner22.py, uncompyle6/scanners/scanner26.py,
|
||||
uncompyle6/scanners/scanner27.py, uncompyle6/scanners/scanner3.py,
|
||||
uncompyle6/scanners/scanner36.py, uncompyle6/scanners/tok.py,
|
||||
uncompyle6/semantics/check_ast.py,
|
||||
uncompyle6/semantics/make_function.py,
|
||||
uncompyle6/semantics/pysource.py, uncompyle6/verify.py,
|
||||
uncompyle6/version.py: Sync with master
|
||||
|
||||
2017-10-10 rocky <rb@dustyfeet.com>
|
||||
|
||||
* : Merge commit '1d7a3c6444eab5a02d899f789f2a57cfdcbc5a84' into
|
||||
python-2.4
|
||||
|
||||
2017-10-10 rocky <rb@dustyfeet.com>
|
||||
|
||||
* ChangeLog, HOW-TO-REPORT-A-BUG.md, NEWS, test/Makefile,
|
||||
uncompyle6/parser.py, uncompyle6/parsers/parse3.py,
|
||||
uncompyle6/scanners/scanner3.py, uncompyle6/semantics/consts.py,
|
||||
uncompyle6/semantics/pysource.py: Get ready for release 2.13.0
|
||||
|
||||
2017-10-10 rocky <rb@dustyfeet.com>
|
||||
|
||||
* HOW-TO-REPORT-A-BUG.md, test/Makefile, uncompyle6/parser.py,
|
||||
uncompyle6/parsers/parse3.py, uncompyle6/scanners/scanner3.py,
|
||||
uncompyle6/semantics/consts.py, uncompyle6/semantics/pysource.py:
|
||||
Improve parse trace. lambda fixes yet again
|
||||
|
||||
2017-10-10 rocky <rb@dustyfeet.com>
|
||||
|
||||
* test/simple_source/branching/02_ifelse_lambda.py,
|
||||
uncompyle6/semantics/consts.py: Address dead code in lambda ifelse
|
||||
|
||||
2017-10-10 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/parsers/parse24.py, uncompyle6/scanners/scanner3.py:
|
||||
Misc bugs
|
||||
|
||||
2017-10-10 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/parsers/parse24.py, uncompyle6/scanners/scanner3.py:
|
||||
Misc bugs
|
||||
|
||||
2017-10-10 R. Bernstein <rocky@users.noreply.github.com>
|
||||
|
||||
* : Merge pull request #131 from rocky/type2kind-rework Adjust for spark-parser 2.7.0 incompatibilities
|
||||
|
||||
2017-10-10 rocky <rb@dustyfeet.com>
|
||||
|
||||
* __pkginfo__.py, pytest/test_grammar.py, pytest/test_pysource.py,
|
||||
uncompyle6/parser.py, uncompyle6/parsers/astnode.py,
|
||||
uncompyle6/parsers/parse2.py, uncompyle6/parsers/parse24.py,
|
||||
uncompyle6/parsers/parse26.py, uncompyle6/parsers/parse27.py,
|
||||
uncompyle6/parsers/parse3.py, uncompyle6/parsers/parse32.py,
|
||||
uncompyle6/parsers/parse34.py, uncompyle6/parsers/parse35.py,
|
||||
uncompyle6/parsers/parse36.py, uncompyle6/parsers/parse37.py,
|
||||
uncompyle6/scanners/scanner22.py, uncompyle6/scanners/scanner26.py,
|
||||
uncompyle6/scanners/scanner27.py, uncompyle6/scanners/scanner3.py,
|
||||
uncompyle6/scanners/tok.py, uncompyle6/semantics/check_ast.py,
|
||||
uncompyle6/semantics/fragments.py,
|
||||
uncompyle6/semantics/make_function.py,
|
||||
uncompyle6/semantics/pysource.py, uncompyle6/verify.py,
|
||||
uncompyle6/version.py: Adjust for spark-parser 2.7.0
|
||||
incompatabilities
|
||||
|
||||
2017-10-05 rocky <rb@dustyfeet.com>
|
||||
|
||||
* test/simple_source/branching/02_ifelse_lambda.py: One more test
|
||||
|
||||
2017-10-05 rocky <rb@dustyfeet.com>
|
||||
|
||||
* .gitignore, pytest/test_grammar.py, uncompyle6/parser.py,
|
||||
uncompyle6/parsers/parse26.py, uncompyle6/parsers/parse27.py,
|
||||
uncompyle6/parsers/parse3.py, uncompyle6/semantics/consts.py,
|
||||
uncompyle6/semantics/pysource.py: Sync with master
|
||||
|
||||
2017-10-05 rocky <rb@dustyfeet.com>
|
||||
|
||||
* : One more test
|
||||
|
||||
2017-10-05 rocky <rb@dustyfeet.com>
|
||||
|
||||
* : commit b3359439f94c136619b198beaecbfce1b827d2db Author: rocky
|
||||
<rb@dustyfeet.com> Date: Thu Oct 5 11:00:55 2017 -0400
|
||||
|
||||
2017-10-03 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/parsers/parse2.py, uncompyle6/parsers/parse24.py,
|
||||
uncompyle6/parsers/parse26.py: handle newer parser reduction
|
||||
behavior
|
||||
|
||||
2017-10-03 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/parsers/parse2.py, uncompyle6/parsers/parse24.py,
|
||||
uncompyle6/parsers/parse26.py: handle newer parser reduction
|
||||
behavior
|
||||
|
||||
2017-10-03 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/semantics/pysource.py: Remove schumutz
|
||||
|
||||
2017-10-03 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/semantics/pysource.py: More table doc tweaks
|
||||
|
||||
2017-10-03 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/semantics/fragments.py,
|
||||
uncompyle6/semantics/pysource.py: Go over table-semantics
|
||||
description yet again
|
||||
|
||||
2017-10-03 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/semantics/fragments.py,
|
||||
uncompyle6/semantics/pysource.py: Go over table-semantics
|
||||
description yet again
|
||||
|
||||
2017-10-02 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/parsers/parse2.py, uncompyle6/parsers/parse3.py: Sync
|
||||
with master
|
||||
|
||||
2017-10-02 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/parsers/parse2.py, uncompyle6/parsers/parse3.py:
|
||||
spark-parser induced changes... reduce rules can be called without token streams.
|
||||
|
||||
2017-09-30 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/parser.py, uncompyle6/scanners/scanner2.py,
|
||||
uncompyle6/scanners/scanner3.py: Document hacky customize arg count
|
||||
better.
|
||||
|
||||
2017-09-26 rocky <rb@dustyfeet.com>
|
||||
|
||||
* README.rst: Word hacking
|
||||
|
||||
2017-09-26 rocky <rb@dustyfeet.com>
|
||||
|
||||
* ChangeLog, NEWS: Get ready for release 2.12.0
|
||||
|
||||
2017-09-26 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/parsers/parse3.py: Annotation field can be unicode... When deparsing Python 3.x from Python 2.
|
||||
|
||||
2017-09-26 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/parsers/parse3.py: No unicode in Python3. but we need it in Python2. The bug was probably introduced as a
|
||||
result of recent Python code type unteroperability canonicalization
|
||||
|
||||
2017-09-26 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/parsers/parse3.py: Pyton 3.1 Annotation args can be
|
||||
unicode?
|
||||
|
||||
2017-09-25 rocky <rb@dustyfeet.com>
|
||||
|
||||
* __pkginfo__.py: Require xdis 3.6.0 or greater
|
||||
|
||||
2017-09-25 rocky <rb@dustyfeet.com>
|
||||
|
||||
* : commit 0654aed6c823d0bb20abdc866481ca5950db72f7 Author: rocky
|
||||
<rb@dustyfeet.com> Date: Thu Sep 21 11:29:17 2017 -0400
|
||||
|
||||
2017-09-25 rocky <rb@dustyfeet.com>
|
||||
|
||||
* : Adjust for xdis opcode JUMP_OPS. release 2.12.0
|
||||
|
||||
2017-09-21 rocky <rb@dustyfeet.com>
|
||||
|
||||
* pytest/test_pysource.py: Python 3 compatibility
|
||||
|
||||
2017-09-21 rocky <rb@dustyfeet.com>
|
||||
|
||||
* pytest/test_pysource.py, uncompyle6/semantics/consts.py,
|
||||
uncompyle6/semantics/fragments.py, uncompyle6/semantics/pysource.py:
|
||||
Unit test for format-specifiers
|
||||
|
||||
2017-09-21 rocky <rb@dustyfeet.com>
|
||||
|
||||
* pytest/test_pysource.py, uncompyle6/semantics/consts.py,
|
||||
uncompyle6/semantics/fragments.py, uncompyle6/semantics/pysource.py:
|
||||
Unit test for format-specifiers And in the process we catch some small bugs
|
||||
|
||||
2017-09-20 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/semantics/fragments.py,
|
||||
uncompyle6/semantics/pysource.py: Tidy pysource and fragments
|
||||
|
||||
2017-09-20 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/semantics/fragments.py,
|
||||
uncompyle6/semantics/pysource.py: Tidy pysource and fragments a
|
||||
little more
|
||||
|
||||
2017-09-20 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/semantics/consts.py: Tidy/regularize table entry
|
||||
formatting
|
||||
|
||||
2017-09-20 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/semantics/consts.py: Tidy/regularize table entry
|
||||
formatting
|
||||
|
||||
2017-09-20 rocky <rb@dustyfeet.com>
|
||||
|
||||
* test/test_pythonlib.py, uncompyle6/semantics/pysource.py: Small
|
||||
fixes test_pyenvlib.py: it is sys.exit(), not exit() pysource.py:
|
||||
reinstate nod type of async_func_call
|
||||
|
||||
2017-09-20 rocky <rb@dustyfeet.com>
|
||||
|
||||
* test/test_pythonlib.py, uncompyle6/semantics/pysource.py: small
|
||||
fixes... test_pythonlib.py: it is sys.exit not exit pysource.py: restore node
|
||||
type on async_call function
|
||||
|
||||
2017-09-20 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/semantics/consts.py, uncompyle6/semantics/pysource.py:
|
||||
More small doc changes
|
||||
|
||||
2017-09-20 rocky <rb@dustyfeet.com>
|
||||
|
||||
* pytest/test_pysource.py, uncompyle6/semantics/pysource.py: Start
|
||||
pysource unit test
|
||||
|
||||
2017-09-20 rocky <rb@dustyfeet.com>
|
||||
|
||||
* pytest/test_pysource.py, uncompyle6/semantics/pysource.py: Update
|
||||
Table-driven info... Start a pysource unit test.
|
||||
|
||||
2017-09-17 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/semantics/fragments.py,
|
||||
uncompyle6/semantics/pysource.py: emgine -> template_engine
|
||||
|
||||
2017-09-17 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/semantics/fragments.py,
|
||||
uncompyle6/semantics/pysource.py: engine -> template_engine
|
||||
|
||||
2017-09-13 rocky <rb@dustyfeet.com>
|
||||
|
||||
* test/Makefile: Need weak-verification on 3.4 for now
|
||||
|
||||
2017-09-10 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/semantics/fragments.py: Revert one of the changes
|
||||
pending a better fix
|
||||
|
||||
2017-09-10 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/semantics/fragments.py,
|
||||
uncompyle6/semantics/pysource.py: More semantic action cleanup
|
||||
|
||||
2017-09-10 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/scanners/scanner3.py, uncompyle6/scanners/tok.py: Match
|
||||
Python 3.4's terms a little names better
|
||||
|
||||
2017-09-09 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/scanners/tok.py: Revert last revert
|
||||
|
||||
2017-09-09 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/scanners/tok.py: Revert last change
|
||||
|
||||
2017-09-09 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/scanners/tok.py: New-style Python classes only, please.
|
||||
|
||||
2017-08-31 rocky <rb@dustyfeet.com>
|
||||
|
||||
* uncompyle6/scanner.py, uncompyle6/scanners/scanner37.py: Skeletal
|
||||
support for Python 3.7 Largely failing though.
|
||||
|
||||
2017-08-31 rocky <rb@dustyfeet.com>
|
||||
|
||||
* : commit 356ea6c7705a557cb3e725d1aca8589dd62b5cdf Author: rocky
|
||||
<rb@dustyfeet.com> Date: Thu Aug 31 09:50:48 2017 -0400
|
||||
|
||||
2017-08-31 rocky <rb@dustyfeet.com>
|
||||
|
||||
* : commit 4d5843851543bfb3c97fc3c49036f1a971fc1d66 Author: rocky
|
||||
<rb@dustyfeet.com> Date: Thu Aug 31 08:53:58 2017 -0400
|
||||
|
||||
2017-08-15 rocky <rb@dustyfeet.com>
|
||||
|
||||
* Makefile: 3.7 support
|
||||
|
||||
2017-08-15 rocky <rb@dustyfeet.com>
|
||||
|
||||
* : commit c54a47b15f85be50d2278aa79fd514eb08580e65 Author: rocky
|
||||
|
17
HISTORY.md
17
HISTORY.md
@@ -98,9 +98,20 @@ so. Then hamled made a few commits earler on, while Eike Siewertsen
|
||||
made a few commits later on. But mostly wibiti, and Guenther
|
||||
Starnberger got the code to where uncompyle2 was around 2012.
|
||||
|
||||
In `uncompyle`, decompilation of python bytecode 2.5 & 2.6 is done by
|
||||
transforming the byte code into a pseudo-2.7 Python bytecode and is
|
||||
based on code from Eloi Vanderbeken.
|
||||
While John Aycock and Hartmut Goebel were well versed in compiler
|
||||
technology, those that have come afterwards don't seem to have been as
|
||||
facile in it. Furthermore, documentation or guidance on how the
|
||||
decompiler code worked, comparison to a conventional compiler
|
||||
pipeline, how to add new constructs, or debug grammars was weak. Some
|
||||
of the grammar tracing and error reporting was a bit weak as well.
|
||||
|
||||
Given this, perhaps it is not surprising that subsequent changes
|
||||
tended to shy away from using the built-in compiler technology
|
||||
mechanisms and addressed problems and extensions by some other means.
|
||||
|
||||
Specifically, in `uncompyle`, decompilation of python bytecode 2.5 & 2.6
|
||||
is done by transforming the byte code into a pseudo-2.7 Python
|
||||
bytecode and is based on code from Eloi Vanderbeken.
|
||||
|
||||
This project, `uncompyle6`, abandons that approach for various
|
||||
reasons. However the main reason is that we need offsets in fragment
|
||||
|
@@ -2,17 +2,98 @@
|
||||
|
||||
## The difficulty of the problem
|
||||
|
||||
There is no Python decompiler yet, that I know about that will
|
||||
decompyle everything. This one probably does the
|
||||
best job of *any* Python decompiler. But it is a constant work in progress: Python keeps changing, and so does its code generation.
|
||||
This decompiler is a constant work in progress: Python keeps
|
||||
changing, and so does its code generation.
|
||||
|
||||
I have found bugs in *every* Python decompiler I have tried. Even
|
||||
those where authors/maintainers claim that they have used it on
|
||||
the entire Python standard library. And I don't mean that
|
||||
the program doesn't come out with the same Python source instructions,
|
||||
but that the program is *semantically* not equivalent.
|
||||
There is no Python decompiler yet that I know about that will
|
||||
decompile everything. Overall, I think this one probably does the best
|
||||
job of *any* Python decompiler that handles such a wide range of
|
||||
versions.
|
||||
|
||||
So it is likely you'll find a mistranslation in decompiling.
|
||||
But at any given time, there are maybe dozens of valid Python bytecode
|
||||
files that I know of that will cause problems. And when I get through
|
||||
those and all the issues of decompiler bugs that are currently logged,
|
||||
I could probably easily find dozens more bugs just by doing a
|
||||
decompile of all the Python bytecode on any one of my
|
||||
computers. Unless you want to help out by _fixing_ bugs, or are
|
||||
willing to do work by isolating and narrowing bugs, don't feel you are
|
||||
doing me a favor by doing scans on your favorite sets of bytecode
|
||||
files.
|
||||
|
||||
In sum, it is not uncommon that you will find a mistranslation in
|
||||
decompiling. Furthermore, you may be expected to do some work in order
|
||||
to have your bug worthy of being considered above other bugs.
|
||||
|
||||
No one is getting paid to work to work on this project, let alone bugs
|
||||
you may have an interest in. If you require decompiling bytecode
|
||||
immediately, consider using a decompilation service.
|
||||
|
||||
## Is it really a bug?
|
||||
|
||||
|
||||
### Do you have valid bytecode?
|
||||
|
||||
As mentioned in README.rst, this project doesn't handle obfuscated
|
||||
code. See README.rst for suggestions for how to remove some kinds of
|
||||
obfuscation.
|
||||
|
||||
Checking if bytecode is valid is pretty simple: disassemble the code.
|
||||
Python comes with a disassembly module called `dis`. A prerequisite
|
||||
module for this package, `xdis` has a cross-python version
|
||||
disassembler.
|
||||
|
||||
### Semantic equivalence vs. exact source code
|
||||
|
||||
Almost all versions of Python can perform some sort of code
|
||||
improvement that can't be undone. In earlier versions of Python it is
|
||||
rare; in later Python versions, it is more common.
|
||||
|
||||
If the code emitted is semantically equivalent, then this isn't a bug.
|
||||
|
||||
|
||||
For example the code might be
|
||||
|
||||
```
|
||||
if a:
|
||||
if b:
|
||||
x = 1
|
||||
```
|
||||
|
||||
and we might produce:
|
||||
|
||||
```
|
||||
if a and b:
|
||||
x = 1
|
||||
```
|
||||
|
||||
These are equivalent. Sometimes
|
||||
|
||||
```
|
||||
else:
|
||||
if ...
|
||||
|
||||
```
|
||||
|
||||
may out as `elif`.
|
||||
|
||||
|
||||
As mentioned in the README. It is possible that Python changes what
|
||||
you write to be more efficient. For example, for:
|
||||
|
||||
|
||||
```
|
||||
if True:
|
||||
x = 5
|
||||
```
|
||||
|
||||
Python will generate code like:
|
||||
|
||||
```
|
||||
x = 5
|
||||
```
|
||||
|
||||
So just because the text isn't the same, does not
|
||||
necessarily mean there's a bug.
|
||||
|
||||
## What to send (minimum requirements)
|
||||
|
||||
@@ -21,13 +102,20 @@ The basic requirement is pretty simple:
|
||||
* Python bytecode
|
||||
* Python source text
|
||||
|
||||
Please don't put files on download services that one has to register
|
||||
for or can't get to by issuing a simple `curl` or `wget`. If you can't
|
||||
attach it to the issue, or create a github gist, then the code you are
|
||||
sending is too large.
|
||||
|
||||
Also try to narrow the bug. See below.
|
||||
|
||||
## What to send (additional helpful information)
|
||||
|
||||
Some kind folks also give the invocation they used and the output
|
||||
which usually includes an error message produced. This is helpful. I
|
||||
can figure out what OS you are running this on and what version of
|
||||
*uncomplye6* was used. Therefore, if you don't provide the input
|
||||
command and the output from that, please give:
|
||||
which usually includes an error message produced. This is
|
||||
helpful. From this, I can figure out what OS you are running this on
|
||||
and what version of *uncomplye6* was used. Therefore, if you don't
|
||||
provide the input command and the output from that, please give:
|
||||
|
||||
* _uncompyle6_ version used
|
||||
* OS that you used this on
|
||||
@@ -48,11 +136,17 @@ Well, you could learn. No one is born into this world knowing how to
|
||||
disassemble Python bytecode. And as Richard Feynman once said, "What
|
||||
one fool can learn, so can another."
|
||||
|
||||
If this is too difficult, or too time consuming, or not of interest to
|
||||
you, then perhaps what require is a decompilation service. [Crazy
|
||||
Compilers](http://www.crazy-compilers.com/decompyle/) offers a
|
||||
byte-code decompiler service for versions of Python up to 2.6. (If
|
||||
there are others around let me know and I'll list them here.)
|
||||
|
||||
## Narrowing the problem
|
||||
|
||||
I don't need or want the entire source code base for which one file or module
|
||||
can't be decompiled. I just need that one file or module only. If
|
||||
there are several files, file a bug report for each file.
|
||||
I don't need or want the entire source code base for which one file or
|
||||
module can't be decompiled. I just need those file(s) or module(s).
|
||||
If there are several files, file a bug report for each file.
|
||||
|
||||
Python modules can get quite large, and usually decompilation problems
|
||||
occur in a single function or maybe the main-line code but not any of
|
||||
@@ -66,3 +160,13 @@ properly on a neighboring version of Python. That is helpful too.
|
||||
|
||||
In sum, the more you can isolate or narrow the problem, the more
|
||||
likley the problem will be fixed and fixed sooner.
|
||||
|
||||
## Confidentiality of Bug Reports
|
||||
|
||||
When you report a bug, you are giving up confidentiality to the source
|
||||
code and the byte code. However, I would imagine that if you have
|
||||
narrowed the problem sufficiently, confidentiality little that
|
||||
remains would not be an issue.
|
||||
|
||||
However feel free to remove any commments, and modify variable names
|
||||
or constants in the source code.
|
||||
|
16
Makefile
16
Makefile
@@ -11,7 +11,7 @@ RM ?= rm
|
||||
LINT = flake8
|
||||
|
||||
#EXTRA_DIST=ipython/ipy_trepan.py trepan
|
||||
PHONY=all check clean pytest check-long dist distclean lint flake8 test rmChangeLog clean_pyc
|
||||
PHONY=all check clean distcheck pytest check-long dist distclean lint flake8 test rmChangeLog clean_pyc
|
||||
|
||||
TEST_TYPES=check-long check-short check-2.7 check-3.4
|
||||
|
||||
@@ -42,9 +42,9 @@ check-3.7: pytest
|
||||
check-2.4 check-2.5 check-2.6:
|
||||
$(MAKE) -C test $@
|
||||
|
||||
#:PyPy 2.6.1 or PyPy 5.0.1
|
||||
#:PyPy 2.6.1 PyPy 5.0.1, or PyPy 5.8.0-beta0
|
||||
# Skip for now
|
||||
2.6 5.0 5.3:
|
||||
2.6 5.0 5.3 5.6 5.8:
|
||||
|
||||
#:PyPy pypy3-2.4.0 Python 3:
|
||||
pypy-3.2 2.4:
|
||||
@@ -60,8 +60,12 @@ clean: clean_pyc
|
||||
(cd test && $(MAKE) clean)
|
||||
|
||||
#: Create source (tarball) and wheel distribution
|
||||
dist:
|
||||
$(PYTHON) ./setup.py sdist bdist_egg
|
||||
dist: distcheck
|
||||
$(PYTHON) ./setup.py sdist bdist_wheel
|
||||
|
||||
# perform some checks on the package via setup.py
|
||||
distcheck:
|
||||
$(PYTHON) ./setup.py check
|
||||
|
||||
#: Remove .pyc files
|
||||
clean_pyc:
|
||||
@@ -89,7 +93,7 @@ bdist_egg:
|
||||
|
||||
|
||||
#: Create binary wheel distribution
|
||||
bdist_wheel:
|
||||
wheel:
|
||||
$(PYTHON) ./setup.py bdist_wheel
|
||||
|
||||
|
||||
|
53
NEWS
53
NEWS
@@ -1,3 +1,56 @@
|
||||
uncompyle6 2.13.3 2017-11-13
|
||||
|
||||
Overall: better 3.6 decompiling and some much needed code refactoring and cleanup
|
||||
|
||||
|
||||
- Start noting names in for template-action names; these are
|
||||
used to check/assert we have the right node type
|
||||
- Simplify <import_from> rule
|
||||
- Pypy 5.80-beta testing tolerance
|
||||
- Start to clean up instruction mangling phase by using 3.6-style instructions
|
||||
rather trying to parse the bytecode array. This largely been done in for versions 3.x;
|
||||
3.0 custom mangling code has been reduced;
|
||||
some 2.x conversion has been done, but more is desired. This make it possible to...
|
||||
- Handle EXTENDED_ARGS better. While relevant to all Python versions it is most noticeable in
|
||||
version 3.6+ where in switching to wordcodes the size of operands has been reduced from 2**16
|
||||
to 2**8. JUMP instruction then often need EXTENDED_ARGS.
|
||||
- Refactor find_jump_targets() with via working of of instructions rather the bytecode array.
|
||||
- use --weak-verify more and additional fuzzing on verify()
|
||||
- fragment parser now ignores errors in nested function definitions; an parameter was
|
||||
added to assist here. Ignoring errors may be okay because the fragment parser often just needs,
|
||||
well, *fragments*.
|
||||
- Distinguish RETURN_VALUE from RETURN_END_IF in exception bodies better in 3.6
|
||||
- bug in 3.x language changes: import queue va import Queue
|
||||
- reinstate some bytecode tests since decompiling has gotten better
|
||||
- Revise how to report a bug
|
||||
|
||||
uncompyle6 2.13.2 2017-10-12
|
||||
|
||||
- Re-release using a more automated approach
|
||||
|
||||
uncompyle6 2.13.1 2017-10-11
|
||||
|
||||
- Re-release because Python 2.4 source uploaded rather than 2.6-3.6
|
||||
|
||||
uncompyle6 2.13.0 2017-10-10
|
||||
|
||||
- Fixes in deparsing lambda expressions
|
||||
- Improve table-semantics descriptions
|
||||
- Document hacky customize arg count better (until we can remove it)
|
||||
- Update to use xdis 3.7.0 or greater
|
||||
|
||||
uncompyle6 2.12.0 2017-09-26
|
||||
|
||||
- Use xdis 3.6.0 or greater now
|
||||
- Small semantic table cleanups
|
||||
- Python 3.4's terms a little names better
|
||||
- Slightly more Python 3.7, but still failing a lot
|
||||
- Cross Python 2/3 compatibility with annotation arguments
|
||||
|
||||
uncompyle6 2.11.5 2017-08-31
|
||||
|
||||
- Skeletal support for Python 3.7
|
||||
|
||||
uncompyle6 2.11.4 2017-08-15
|
||||
|
||||
* scanner and parser now allow 3-part version string lookups,
|
||||
|
@@ -4,7 +4,7 @@ uncompyle6
|
||||
==========
|
||||
|
||||
A native Python cross-version Decompiler and Fragment Decompiler.
|
||||
Follows in the tradition of decompyle, uncompyle, and uncompyle2.
|
||||
The successor to decompyle, uncompyle, and uncompyle2.
|
||||
|
||||
|
||||
Introduction
|
||||
|
@@ -39,8 +39,8 @@ entry_points = {
|
||||
'pydisassemble=uncompyle6.bin.pydisassemble:main',
|
||||
]}
|
||||
ftp_url = None
|
||||
install_requires = ['spark-parser >= 1.6.1, < 1.7.0',
|
||||
'xdis >= 3.5.5, < 3.6.0']
|
||||
install_requires = ['spark-parser >= 1.7.1, < 1.8.0',
|
||||
'xdis >= 3.6.1, < 3.7.0']
|
||||
license = 'MIT'
|
||||
mailing_list = 'python-debugger@googlegroups.com'
|
||||
modname = 'uncompyle6'
|
||||
|
11
admin-tools/README.md
Normal file
11
admin-tools/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
Making a release is a somewhat tedious process so I've automated it a little
|
||||
|
||||
|
||||
Here are tools that I, rocky, use to check and build a distribution.
|
||||
|
||||
They are customized to my environment:
|
||||
- I use pyenv to various Python versions installed
|
||||
- I have git repos for xdis, and spark parser at the same level as uncompyle6
|
||||
|
||||
There may be other rocky-specific things that need customization.
|
||||
how-to-make-a-release.txt has overall how I make a release
|
26
admin-tools/check-newer-versions.sh
Normal file
26
admin-tools/check-newer-versions.sh
Normal file
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
function finish {
|
||||
cd $owd
|
||||
}
|
||||
|
||||
# FIXME put some of the below in a common routine
|
||||
owd=$(pwd)
|
||||
trap finish EXIT
|
||||
|
||||
cd $(dirname ${BASH_SOURCE[0]})
|
||||
if ! source ./pyenv-newer-versions ; then
|
||||
exit $?
|
||||
fi
|
||||
if ! source ./setup-master.sh ; then
|
||||
exit $?
|
||||
fi
|
||||
cd ..
|
||||
for version in $PYVERSIONS; do
|
||||
if ! pyenv local $version ; then
|
||||
exit $?
|
||||
fi
|
||||
make clean && pip install -e .
|
||||
if ! make check; then
|
||||
exit $?
|
||||
fi
|
||||
done
|
27
admin-tools/check-older-versions.sh
Normal file
27
admin-tools/check-older-versions.sh
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
function finish {
|
||||
cd $owd
|
||||
}
|
||||
owd=$(pwd)
|
||||
trap finish EXIT
|
||||
|
||||
cd $(dirname ${BASH_SOURCE[0]})
|
||||
if ! source ./pyenv-older-versions ; then
|
||||
exit $?
|
||||
fi
|
||||
if ! source ./setup-python-2.4.sh ; then
|
||||
exit $?
|
||||
fi
|
||||
|
||||
PYVERSIONS='2.7.14 2.6.9 3.3.6 3.4.2 3.5.4 3.6.3'
|
||||
|
||||
cd ..
|
||||
for version in $PYVERSIONS; do
|
||||
if ! pyenv local $version ; then
|
||||
exit $?
|
||||
fi
|
||||
make clean && python setup.py develop
|
||||
if ! make check ; then
|
||||
exit $?
|
||||
fi
|
||||
done
|
88
admin-tools/how-to-make-a-release.md
Normal file
88
admin-tools/how-to-make-a-release.md
Normal file
@@ -0,0 +1,88 @@
|
||||
<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-refresh-toc -->
|
||||
**Table of Contents**
|
||||
|
||||
- [Get latest sources:](#get-latest-sources)
|
||||
- [Change version in uncompyle6/version.py. Then:](#change-version-in-uncompyle6versionpy-then)
|
||||
- [Update ChangeLog:](#update-changelog)
|
||||
- [Update NEWS from ChangeLog. Then:](#update-news-from-changelog-then)
|
||||
- [Make sure pyenv is running and check newer versions](#make-sure-pyenv-is-running-and-check-newer-versions)
|
||||
- [Switch to python-2.4, sync that up and build that first since it creates a tarball which we don't want.](#switch-to-python-24-sync-that-up-and-build-that-first-since-it-creates-a-tarball-which-we-dont-want)
|
||||
- [Update NEWS from master branch](#update-news-from-master-branch)
|
||||
- [Check against all versions](#check-against-all-versions)
|
||||
- [Make packages and tag](#make-packages-and-tag)
|
||||
- [Upload single package and look at Rst Formating](#upload-single-package-and-look-at-rst-formating)
|
||||
- [Upload rest of versions](#upload-rest-of-versions)
|
||||
- [Push tags:](#push-tags)
|
||||
|
||||
<!-- markdown-toc end -->
|
||||
# Get latest sources:
|
||||
|
||||
$ . ./admin-tool/update-sources.sh
|
||||
|
||||
# Change version in uncompyle6/version.py. Then:
|
||||
|
||||
$ emacs uncompyle6/version.py
|
||||
$ source uncompyle6/version.py
|
||||
$ echo $VERSION
|
||||
$ git commit -m"Get ready for release $VERSION" .
|
||||
|
||||
# Update ChangeLog:
|
||||
|
||||
$ make ChangeLog
|
||||
|
||||
# Update NEWS from ChangeLog. Then:
|
||||
|
||||
$ emacs NEWS
|
||||
$ make check
|
||||
$ git commit --amend .
|
||||
$ git push # get CI testing going early
|
||||
|
||||
# Make sure pyenv is running and check newer versions
|
||||
|
||||
$ pyenv local && source admin-tools/check-newer-versions.sh
|
||||
|
||||
# Switch to python-2.4, sync that up and build that first since it creates a tarball which we don't want.
|
||||
|
||||
$ source admin-tools/setup-python-2.4.sh
|
||||
$ rm ChangeLog
|
||||
|
||||
# $ git merge master ?
|
||||
|
||||
# Update NEWS from master branch
|
||||
|
||||
$ git commit -m"Get ready for release $VERSION" .
|
||||
|
||||
# Check against all versions
|
||||
|
||||
$ source admin-tools/check-older-versions.sh
|
||||
$ source admin-tools/check-newer-versions.sh
|
||||
|
||||
# Make packages and tag
|
||||
|
||||
$ admin-tools/make-dist-older.sh
|
||||
$ git tag release-python-2.4-$VERSION
|
||||
|
||||
$ admin-tools/make-dist-newer.sh
|
||||
$ git tag release-$VERSION
|
||||
|
||||
# Upload single package and look at Rst Formating
|
||||
|
||||
$ twine upload dist/uncompyle6-${VERSION}-py3.3.egg
|
||||
|
||||
# Upload rest of versions
|
||||
|
||||
$ twine upload dist/uncompyle6-${VERSION}*
|
||||
|
||||
# Push tags:
|
||||
|
||||
$ git push --tags
|
||||
|
||||
# Check on a VM
|
||||
|
||||
$ cd /virtual/vagrant/virtual/vagrant/ubuntu-zesty
|
||||
$ vagrant up
|
||||
$ vagrant ssh
|
||||
$ pyenv local 3.5.2
|
||||
$ pip install --upgrade uncompyle6
|
||||
$ exit
|
||||
$ vagrant halt
|
46
admin-tools/how-to-make-a-release.txt
Normal file
46
admin-tools/how-to-make-a-release.txt
Normal file
@@ -0,0 +1,46 @@
|
||||
git pull
|
||||
|
||||
Change version in uncompyle6/version.py
|
||||
source uncompyle6/version.py
|
||||
echo $VERSION
|
||||
git commit -m"Get ready for release $VERSION" .
|
||||
|
||||
Update ChangeLog:
|
||||
make ChangeLog
|
||||
|
||||
Update NEWS from ChangeLog
|
||||
make check
|
||||
|
||||
git commit --amend .
|
||||
|
||||
git push
|
||||
|
||||
Make sure pyenv is running
|
||||
# Pyenv
|
||||
|
||||
source admin-tools/check-newer-versions.sh
|
||||
|
||||
|
||||
# Switch to python-2.4 and build that first...
|
||||
source admin-tools/setup-python-2.4
|
||||
|
||||
rm ChangeLog
|
||||
git merge master
|
||||
|
||||
Update NEWS from master branch
|
||||
|
||||
git commit -m"Get ready for release $VERSION" .
|
||||
|
||||
source admin-tools/check-older-versions.sh
|
||||
source admin-tools/check-newer-versions.sh
|
||||
|
||||
make-dist-older.sh
|
||||
|
||||
git tag release-python-2.4-$VERSION
|
||||
|
||||
./make-dist-newer.sh
|
||||
|
||||
git tag release-$VERSION
|
||||
|
||||
|
||||
twine upload dist/uncompyle6-${VERSION}*
|
38
admin-tools/make-dist-newer.sh
Executable file
38
admin-tools/make-dist-newer.sh
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/bin/bash
|
||||
PACKAGE=uncompyle6
|
||||
|
||||
# FIXME put some of the below in a common routine
|
||||
function finish {
|
||||
cd $owd
|
||||
}
|
||||
|
||||
cd $(dirname ${BASH_SOURCE[0]})
|
||||
owd=$(pwd)
|
||||
trap finish EXIT
|
||||
|
||||
if ! source ./pyenv-newer-versions ; then
|
||||
exit $?
|
||||
fi
|
||||
if ! source ./setup-master.sh ; then
|
||||
exit $?
|
||||
fi
|
||||
|
||||
cd ..
|
||||
source $PACKAGE/version.py
|
||||
echo $VERSION
|
||||
|
||||
for pyversion in $PYVERSIONS; do
|
||||
if ! pyenv local $pyversion ; then
|
||||
exit $?
|
||||
fi
|
||||
# pip bdist_egg create too-general wheels. So
|
||||
# we narrow that by moving the generated wheel.
|
||||
|
||||
# Pick out first two number of version, e.g. 3.5.1 -> 35
|
||||
first_two=$(echo $pyversion | cut -d'.' -f 1-2 | sed -e 's/\.//')
|
||||
rm -fr build
|
||||
python setup.py bdist_egg bdist_wheel
|
||||
mv -v dist/${PACKAGE}-$VERSION-{py2.py3,py$first_two}-none-any.whl
|
||||
done
|
||||
|
||||
python ./setup.py sdist
|
39
admin-tools/make-dist-older.sh
Executable file
39
admin-tools/make-dist-older.sh
Executable file
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
PACKAGE=uncompyle6
|
||||
|
||||
# FIXME put some of the below in a common routine
|
||||
function finish {
|
||||
cd $owd
|
||||
}
|
||||
owd=$(pwd)
|
||||
trap finish EXIT
|
||||
|
||||
cd $(dirname ${BASH_SOURCE[0]})
|
||||
if ! source ./pyenv-older-versions ; then
|
||||
exit $?
|
||||
fi
|
||||
if ! source ./setup-python-2.4.sh ; then
|
||||
exit $?
|
||||
fi
|
||||
|
||||
cd ..
|
||||
source $PACKAGE/version.py
|
||||
echo $VERSION
|
||||
|
||||
for pyversion in $PYVERSIONS; do
|
||||
if ! pyenv local $pyversion ; then
|
||||
exit $?
|
||||
fi
|
||||
|
||||
rm -fr build
|
||||
python setup.py bdist_egg
|
||||
done
|
||||
|
||||
# Pypi can only have one source tarball.
|
||||
# Tarballs can get created from the above setup, so make sure to remove them since we want
|
||||
# the tarball from master.
|
||||
|
||||
tarball=dist/${PACKAGE}-$VERSION-tar.gz
|
||||
if [[ -f $tarball ]]; then
|
||||
rm -v dist/${PACKAGE}-$VERSION-tar.gz
|
||||
fi
|
6
admin-tools/pyenv-newer-versions
Normal file
6
admin-tools/pyenv-newer-versions
Normal file
@@ -0,0 +1,6 @@
|
||||
# -*- shell-script -*-
|
||||
if [[ $0 == ${BASH_SOURCE[0]} ]] ; then
|
||||
echo "This script should be *sourced* rather than run directly through bash"
|
||||
exit 1
|
||||
fi
|
||||
export PYVERSIONS='3.5.2 3.6.2 2.6.9 3.3.6 2.7.13 3.4.2'
|
6
admin-tools/pyenv-older-versions
Normal file
6
admin-tools/pyenv-older-versions
Normal file
@@ -0,0 +1,6 @@
|
||||
# -*- shell-script -*-
|
||||
if [[ $0 == ${BASH_SOURCE[0]} ]] ; then
|
||||
echo "This script should be *sourced* rather than run directly through bash"
|
||||
exit 1
|
||||
fi
|
||||
export PYVERSIONS='2.4.6 2.5.6'
|
22
admin-tools/setup-master.sh
Normal file
22
admin-tools/setup-master.sh
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
PYTHON_VERSION=3.6.3
|
||||
|
||||
# FIXME put some of the below in a common routine
|
||||
function finish {
|
||||
cd $owd
|
||||
}
|
||||
|
||||
export PATH=$HOME/.pyenv/bin/pyenv:$PATH
|
||||
owd=$(pwd)
|
||||
bs=${BASH_SOURCE[0]}
|
||||
if [[ $0 == $bs ]] ; then
|
||||
echo "This script should be *sourced* rather than run directly through bash"
|
||||
exit 1
|
||||
fi
|
||||
mydir=$(dirname $bs)
|
||||
fulldir=$(readlink -f $mydir)
|
||||
cd $fulldir/..
|
||||
(cd ../python-spark && git checkout master && pyenv local $PYTHON_VERSION) && \
|
||||
(cd ../python-xdis && git checkout master && pyenv local $PYTHON_VERSION) && \
|
||||
git checkout master && pyenv local $PYTHON_VERSION
|
||||
cd $owd
|
16
admin-tools/setup-python-2.4.sh
Normal file
16
admin-tools/setup-python-2.4.sh
Normal file
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
PYTHON_VERSION=2.4.6
|
||||
|
||||
owd=$(pwd)
|
||||
bs=${BASH_SOURCE[0]}
|
||||
if [[ $0 == $bs ]] ; then
|
||||
echo "This script should be *sourced* rather than run directly through bash"
|
||||
exit 1
|
||||
fi
|
||||
mydir=$(dirname $bs)
|
||||
fulldir=$(readlink -f $mydir)
|
||||
cd $fulldir/..
|
||||
(cd ../python-spark && git checkout python-2.4 && pyenv local $PYTHON_VERSION) && \
|
||||
(cd ../python-xdis && git checkout python-2.4 && pyenv local $PYTHON_VERSION) && \
|
||||
git checkout python-2.4 && pyenv local $PYTHON_VERSION
|
||||
cd $owd
|
3
admin-tools/update-sources.sh
Executable file
3
admin-tools/update-sources.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
cd $(dirname ${BASH_SOURCE[0]})/..
|
||||
git pull
|
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
from uncompyle6 import PYTHON_VERSION, IS_PYPY
|
||||
from uncompyle6.scanner import get_scanner
|
||||
from xdis.bytecode import Bytecode
|
||||
from array import array
|
||||
def bug(state, slotstate):
|
||||
if state:
|
||||
@@ -28,12 +29,17 @@ def test_if_in_for():
|
||||
scan.build_lines_data(code, n)
|
||||
scan.build_prev_op(n)
|
||||
fjt = scan.find_jump_targets(False)
|
||||
assert {15: [3], 69: [66], 63: [18]} == fjt
|
||||
assert scan.structs == \
|
||||
[{'start': 0, 'end': 72, 'type': 'root'},
|
||||
{'start': 15, 'end': 66, 'type': 'if-then'},
|
||||
{'start': 31, 'end': 59, 'type': 'for-loop'},
|
||||
{'start': 62, 'end': 63, 'type': 'for-else'}]
|
||||
|
||||
## FIXME: the data below is wrong.
|
||||
## we get different results currenty as well.
|
||||
## We need to probably fix both the code
|
||||
## and the test below
|
||||
# assert {15: [3], 69: [66], 63: [18]} == fjt
|
||||
# assert scan.structs == \
|
||||
# [{'start': 0, 'end': 72, 'type': 'root'},
|
||||
# {'start': 15, 'end': 66, 'type': 'if-then'},
|
||||
# {'start': 31, 'end': 59, 'type': 'for-loop'},
|
||||
# {'start': 62, 'end': 63, 'type': 'for-else'}]
|
||||
|
||||
code = bug_loop.__code__
|
||||
n = scan.setup_code(code)
|
||||
@@ -52,9 +58,11 @@ def test_if_in_for():
|
||||
{'start': 48, 'end': 67, 'type': 'while-loop'}]
|
||||
|
||||
elif 3.2 < PYTHON_VERSION <= 3.4:
|
||||
bytecode = Bytecode(code, scan.opc)
|
||||
scan.code = array('B', code.co_code)
|
||||
scan.build_lines_data(code)
|
||||
scan.build_prev_op()
|
||||
scan.insts = list(bytecode)
|
||||
fjt = scan.find_jump_targets(False)
|
||||
assert {69: [66], 63: [18]} == fjt
|
||||
assert scan.structs == \
|
||||
|
@@ -11,15 +11,16 @@ def test_grammar():
|
||||
remain_tokens = set([re.sub('_CONT$','', t) for t in remain_tokens])
|
||||
remain_tokens = set(remain_tokens) - opcode_set
|
||||
assert remain_tokens == set([]), \
|
||||
"Remaining tokens %s\n====\n%s" % (remain_tokens, p.dumpGrammar())
|
||||
"Remaining tokens %s\n====\n%s" % (remain_tokens, p.dump_grammar())
|
||||
|
||||
p = get_python_parser(PYTHON_VERSION, is_pypy=IS_PYPY)
|
||||
lhs, rhs, tokens, right_recursive = p.checkSets()
|
||||
lhs, rhs, tokens, right_recursive = p.check_sets()
|
||||
expect_lhs = set(['expr1024', 'pos_arg'])
|
||||
unused_rhs = set(['build_list', 'call_function', 'mkfunc',
|
||||
'mklambda',
|
||||
'unpack', 'unpack_list'])
|
||||
expect_right_recursive = [['designList', ('designator', 'DUP_TOP', 'designList')]]
|
||||
expect_right_recursive = frozenset([('designList',
|
||||
('designator', 'DUP_TOP', 'designList'))])
|
||||
if PYTHON3:
|
||||
expect_lhs.add('load_genexpr')
|
||||
|
||||
@@ -39,13 +40,14 @@ def test_grammar():
|
||||
s = get_scanner(PYTHON_VERSION, IS_PYPY)
|
||||
ignore_set = set(
|
||||
"""
|
||||
JUMP_BACK CONTINUE RETURN_END_IF
|
||||
JUMP_BACK CONTINUE
|
||||
COME_FROM COME_FROM_EXCEPT
|
||||
COME_FROM_EXCEPT_CLAUSE
|
||||
COME_FROM_LOOP COME_FROM_WITH
|
||||
COME_FROM_FINALLY ELSE
|
||||
LOAD_GENEXPR LOAD_ASSERT LOAD_SETCOMP LOAD_DICTCOMP
|
||||
LAMBDA_MARKER RETURN_LAST
|
||||
LAMBDA_MARKER
|
||||
RETURN_END_IF RETURN_END_IF_LAMBDA RETURN_VALUE_LAMBDA RETURN_LAST
|
||||
""".split())
|
||||
if 2.6 <= PYTHON_VERSION <= 2.7:
|
||||
opcode_set = set(s.opc.opname).union(ignore_set)
|
||||
|
172
pytest/test_pysource.py
Normal file
172
pytest/test_pysource.py
Normal file
@@ -0,0 +1,172 @@
|
||||
from uncompyle6 import PYTHON3
|
||||
from uncompyle6.semantics.consts import (
|
||||
escape, NONE,
|
||||
# RETURN_NONE, PASS, RETURN_LOCALS
|
||||
)
|
||||
|
||||
if PYTHON3:
|
||||
from io import StringIO
|
||||
else:
|
||||
from StringIO import StringIO
|
||||
|
||||
from uncompyle6.semantics.pysource import SourceWalker as SourceWalker
|
||||
|
||||
def test_template_engine():
|
||||
s = StringIO()
|
||||
sw = SourceWalker(2.7, s, None)
|
||||
sw.ast = NONE
|
||||
sw.template_engine(('--%c--', 0), NONE)
|
||||
print(sw.f.getvalue())
|
||||
assert sw.f.getvalue() == '--None--'
|
||||
# FIXME: and so on...
|
||||
|
||||
from uncompyle6.semantics.consts import (
|
||||
TABLE_R, TABLE_DIRECT,
|
||||
)
|
||||
|
||||
from uncompyle6.semantics.fragments import (
|
||||
TABLE_DIRECT_FRAGMENT,
|
||||
)
|
||||
|
||||
skip_for_now = "DELETE_DEREF".split()
|
||||
|
||||
def test_tables():
|
||||
for t, name, fragment in (
|
||||
(TABLE_DIRECT, 'TABLE_DIRECT', False),
|
||||
(TABLE_R, 'TABLE_R', False),
|
||||
(TABLE_DIRECT_FRAGMENT, 'TABLE_DIRECT_FRAGMENT', True)):
|
||||
for k, entry in t.iteritems():
|
||||
if k in skip_for_now:
|
||||
continue
|
||||
fmt = entry[0]
|
||||
arg = 1
|
||||
i = 0
|
||||
m = escape.search(fmt)
|
||||
print("%s[%s]" % (name, k))
|
||||
while m:
|
||||
i = m.end()
|
||||
typ = m.group('type') or '{'
|
||||
if typ in frozenset(['%', '+', '-', '|', ',', '{']):
|
||||
# No args
|
||||
pass
|
||||
elif typ in frozenset(['c', 'p', 'P', 'C', 'D']):
|
||||
# One arg - should be int or tuple of int
|
||||
if typ == 'c':
|
||||
item = entry[arg]
|
||||
if isinstance(item, tuple):
|
||||
assert isinstance(item[1], str), (
|
||||
"%s[%s][%d] kind %s is '%s' should be str but is %s. "
|
||||
"Full entry: %s" %
|
||||
(name, k, arg, typ, item[1], type(item[1]), entry)
|
||||
)
|
||||
item = item[0]
|
||||
assert isinstance(item, int), (
|
||||
"%s[%s][%d] kind %s is '%s' should be an int but is %s. "
|
||||
"Full entry: %s" %
|
||||
(name, k, arg, typ, item, type(item), entry)
|
||||
)
|
||||
elif typ in frozenset(['C', 'D']):
|
||||
tup = entry[arg]
|
||||
assert isinstance(tup, tuple), (
|
||||
"%s[%s][%d] type %s is %s should be an tuple but is %s. "
|
||||
"Full entry: %s" %
|
||||
(name, k, arg, typ, entry[arg], type(entry[arg]), entry)
|
||||
)
|
||||
assert len(tup) == 3
|
||||
for j, x in enumerate(tup[:-1]):
|
||||
assert isinstance(x, int), (
|
||||
"%s[%s][%d][%d] type %s is %s should be an tuple but is %s. "
|
||||
"Full entry: %s" %
|
||||
(name, k, arg, j, typ, x, type(x), entry)
|
||||
)
|
||||
assert isinstance(tup[-1], str) or tup[-1] is None, (
|
||||
"%s[%s][%d][%d] sep type %s is %s should be an string but is %s. "
|
||||
"Full entry: %s" %
|
||||
(name, k, arg, j, typ, tup[-1], type(x), entry)
|
||||
)
|
||||
|
||||
elif typ == 'P':
|
||||
tup = entry[arg]
|
||||
assert isinstance(tup, tuple), (
|
||||
"%s[%s][%d] type %s is %s should be an tuple but is %s. "
|
||||
"Full entry: %s" %
|
||||
(name, k, arg, typ, entry[arg], type(entry[arg]), entry)
|
||||
)
|
||||
assert len(tup) == 4
|
||||
for j, x in enumerate(tup[:-2]):
|
||||
assert isinstance(x, int), (
|
||||
"%s[%s][%d][%d] type %s is '%s' should be an tuple but is %s. "
|
||||
"Full entry: %s" %
|
||||
(name, k, arg, j, typ, x, type(x), entry)
|
||||
)
|
||||
assert isinstance(tup[-2], str), (
|
||||
"%s[%s][%d][%d] sep type %s is '%s' should be an string but is %s. "
|
||||
"Full entry: %s" %
|
||||
(name, k, arg, j, typ, x, type(x), entry)
|
||||
)
|
||||
assert isinstance(tup[1], int), (
|
||||
"%s[%s][%d][%d] prec type %s is '%s' should be an int but is %s. "
|
||||
"Full entry: %s" %
|
||||
(name, k, arg, j, typ, x, type(x), entry)
|
||||
)
|
||||
|
||||
else:
|
||||
# Should be a tuple which contains only ints
|
||||
tup = entry[arg]
|
||||
assert isinstance(tup, tuple), (
|
||||
"%s[%s][%d] type %s is '%s' should be an tuple but is %s. "
|
||||
"Full entry: %s" %
|
||||
(name, k, arg, typ, entry[arg], type(entry[arg]), entry)
|
||||
)
|
||||
assert len(tup) == 2
|
||||
for j, x in enumerate(tup):
|
||||
assert isinstance(x, int), (
|
||||
"%s[%s][%d][%d] type '%s' is '%s should be an int but is %s. Full entry: %s" %
|
||||
(name, k, arg, j, typ, x, type(x), entry)
|
||||
)
|
||||
pass
|
||||
arg += 1
|
||||
elif typ in frozenset(['r']) and fragment:
|
||||
pass
|
||||
elif typ == 'b' and fragment:
|
||||
assert isinstance(entry[arg], int), (
|
||||
"%s[%s][%d] type %s is '%s' should be an int but is %s. "
|
||||
"Full entry: %s" %
|
||||
(name, k, arg, typ, entry[arg], type(entry[arg]), entry)
|
||||
)
|
||||
arg += 1
|
||||
elif typ == 'x' and fragment:
|
||||
tup = entry[arg]
|
||||
assert isinstance(tup, tuple), (
|
||||
"%s[%s][%d] type %s is '%s' should be an tuple but is %s. "
|
||||
"Full entry: %s" %
|
||||
(name, k, arg, typ, entry[arg], type(entry[arg]), entry)
|
||||
)
|
||||
assert len(tup) == 2
|
||||
assert isinstance(tup[0], int), (
|
||||
"%s[%s][%d] source type %s is '%s' should be an int but is %s. "
|
||||
"Full entry: %s" %
|
||||
(name, k, arg, typ, entry[arg], type(entry[arg]), entry)
|
||||
)
|
||||
assert isinstance(tup[1], tuple), (
|
||||
"%s[%s][%d] dest type %s is '%s' should be an tuple but is %s. "
|
||||
"Full entry: %s" %
|
||||
(name, k, arg, typ, entry[arg], type(entry[arg]), entry)
|
||||
)
|
||||
for j, x in enumerate(tup[1]):
|
||||
assert isinstance(x, int), (
|
||||
"%s[%s][%d][%d] type %s is %s should be an int but is %s. Full entry: %s" %
|
||||
(name, k, arg, j, typ, x, type(x), entry)
|
||||
)
|
||||
arg += 1
|
||||
pass
|
||||
else:
|
||||
assert False, (
|
||||
"%s[%s][%d] type %s is not known. Full entry: %s" %
|
||||
(name, k, arg, typ, entry)
|
||||
)
|
||||
m = escape.search(fmt, i)
|
||||
pass
|
||||
assert arg == len(entry), (
|
||||
"%s[%s] arg %d should be length of entry %d. Full entry: %s" %
|
||||
(name, k, arg, len(entry), entry))
|
2
setup.py
2
setup.py
@@ -24,6 +24,6 @@ setup(
|
||||
py_modules = py_modules,
|
||||
test_suite = 'nose.collector',
|
||||
url = web,
|
||||
tests_require = ['nose>=1.0'],
|
||||
tests_require = ['nose>=1.0'],
|
||||
version = VERSION,
|
||||
zip_safe = zip_safe)
|
||||
|
@@ -39,7 +39,7 @@ check-3.3: check-bytecode
|
||||
|
||||
#: Run working tests from Python 3.4
|
||||
check-3.4: check-bytecode check-3.4-ok check-2.7-ok
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.4 --verify $(COMPILE)
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.4 --weak-verify $(COMPILE)
|
||||
|
||||
#: Run working tests from Python 3.5
|
||||
check-3.5: check-bytecode
|
||||
@@ -47,7 +47,11 @@ check-3.5: check-bytecode
|
||||
|
||||
#: Run working tests from Python 3.6
|
||||
check-3.6: check-bytecode
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.6 --verify $(COMPILE)
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.6 --weak-verify $(COMPILE)
|
||||
|
||||
# FIXME
|
||||
#: this is called when running under pypy3.5-5.8.0 or pypy2-5.6.0
|
||||
5.8 5.6:
|
||||
|
||||
#: Check deparsing only, but from a different Python version
|
||||
check-disasm:
|
||||
@@ -155,7 +159,7 @@ check-2.4-ok:
|
||||
|
||||
#: Run longer Python 2.6's lib files known to be okay
|
||||
check-2.6-ok:
|
||||
$(PYTHON) test_pythonlib.py --ok-2.6 --verify $(COMPILE)
|
||||
$(PYTHON) test_pythonlib.py --ok-2.6 --weak-verify $(COMPILE)
|
||||
|
||||
#: Run longer Python 2.7's lib files known to be okay
|
||||
check-2.7-ok:
|
||||
|
BIN
test/bytecode_2.7/02_ifelse_lambda.pyc
Normal file
BIN
test/bytecode_2.7/02_ifelse_lambda.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
test/bytecode_3.5/04_importlist.pyc
Normal file
BIN
test/bytecode_3.5/04_importlist.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
test/bytecode_3.6/02_ifelse_lambda.pyc
Normal file
BIN
test/bytecode_3.6/02_ifelse_lambda.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.6/04_importlist.pyc
Normal file
BIN
test/bytecode_3.6/04_importlist.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.6/10_extended_arg_loop.pyc
Normal file
BIN
test/bytecode_3.6/10_extended_arg_loop.pyc
Normal file
Binary file not shown.
19
test/simple_source/branching/02_ifelse_lambda.py
Normal file
19
test/simple_source/branching/02_ifelse_lambda.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# We have to do contortions here because
|
||||
# lambda's have to be more or less on a line
|
||||
|
||||
f = lambda x: 1 if x<2 else 3
|
||||
f(5)
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
|
||||
# If that wasn't enough ...
|
||||
# Python will create dead code
|
||||
# in the below. So we must make sure
|
||||
# not to include the else expression
|
||||
|
||||
g = lambda: 1 if True else 3
|
||||
g()
|
||||
|
||||
h = lambda: 1 if False else 3
|
||||
h()
|
||||
>>>>>>> master
|
7
test/simple_source/bug35/04_importlist.py
Normal file
7
test/simple_source/bug35/04_importlist.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# Had bug in 3.x in not having semantic importlist rule
|
||||
def main(osp, Mfile, mainpyfile, dbg=None):
|
||||
try:
|
||||
from xdis import load_module, PYTHON_VERSION, IS_PYPY
|
||||
return PYTHON_VERSION, IS_PYPY, load_module
|
||||
except:
|
||||
pass
|
49
test/simple_source/bug36/10_extended_arg_loop.py
Normal file
49
test/simple_source/bug36/10_extended_arg_loop.py
Normal file
@@ -0,0 +1,49 @@
|
||||
# Bug in 3.6 has to do with parsing jumps where
|
||||
# the offset is more than 256 bytes so an EXTENDED_ARG
|
||||
# instruction is inserted. find_jump_targets() and
|
||||
# detect_control_flow need to be able to work in the presence
|
||||
# of EXTENDED_ARG.
|
||||
|
||||
# This is a problem theoretically in Python before 3.6
|
||||
# but since offsets are very large it isn't noticed.
|
||||
|
||||
# Code is simplified from trepan2/trepan/cli.py
|
||||
import sys
|
||||
def main(dbg=None, sys_argv=list(sys.argv)):
|
||||
|
||||
if sys_argv:
|
||||
mainpyfile = None
|
||||
else:
|
||||
mainpyfile = "10"
|
||||
sys.path[0] = "20"
|
||||
|
||||
while True:
|
||||
try:
|
||||
if dbg.program_sys_argv and mainpyfile:
|
||||
normal_termination = dbg.run_script(mainpyfile)
|
||||
if not normal_termination: break
|
||||
else:
|
||||
dbg.core.execution_status = 'No program'
|
||||
dbg.core.processor.process_commands()
|
||||
pass
|
||||
|
||||
dbg.core.execution_status = 'Terminated'
|
||||
dbg.intf[-1].msg("The program finished - quit or restart")
|
||||
dbg.core.processor.process_commands()
|
||||
except IOError:
|
||||
break
|
||||
except RuntimeError:
|
||||
dbg.core.execution_status = 'Restart requested'
|
||||
if dbg.program_sys_argv:
|
||||
sys.argv = list(dbg.program_sys_argv)
|
||||
part1 = ('Restarting %s with arguments:' %
|
||||
dbg.core.filename(mainpyfile))
|
||||
args = ' '.join(dbg.program_sys_argv[1:])
|
||||
dbg.intf[-1].msg(args + part1)
|
||||
else: break
|
||||
except SystemExit:
|
||||
break
|
||||
pass
|
||||
|
||||
sys.argv = 5
|
||||
return
|
@@ -169,13 +169,13 @@ def do_tests(src_dir, obj_patterns, target_dir, opts):
|
||||
main(src_dir, target_dir, files, [],
|
||||
do_verify=opts['do_verify'])
|
||||
if failed_files != 0:
|
||||
exit(2)
|
||||
sys.exit(2)
|
||||
elif failed_verify != 0:
|
||||
exit(3)
|
||||
sys.exit(3)
|
||||
|
||||
except (KeyboardInterrupt, OSError):
|
||||
print()
|
||||
exit(1)
|
||||
sys.exit(1)
|
||||
if test_opts['rmtree']:
|
||||
parent_dir = os.path.dirname(target_dir)
|
||||
print("Everything good, removing %s" % parent_dir)
|
||||
|
@@ -1,12 +1,12 @@
|
||||
#!/usr/bin/env python
|
||||
# Mode: -*- python -*-
|
||||
#
|
||||
# Copyright (c) 2015-2016 by Rocky Bernstein
|
||||
# Copyright (c) 2015-2017 by Rocky Bernstein
|
||||
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
||||
#
|
||||
import sys, os, getopt, time
|
||||
|
||||
program, ext = os.path.splitext(os.path.basename(__file__))
|
||||
program = 'uncompyle6'
|
||||
|
||||
__doc__ = """
|
||||
Usage:
|
||||
@@ -174,7 +174,7 @@ def main_bin():
|
||||
try:
|
||||
from Queue import Empty
|
||||
except ImportError:
|
||||
from Queue import Empty
|
||||
from queue import Empty
|
||||
|
||||
fqueue = Queue(len(files)+numproc)
|
||||
for f in files:
|
||||
|
@@ -216,7 +216,7 @@ def main(in_base, out_base, files, codes, outfile=None,
|
||||
if not current_outfile:
|
||||
mess = '\n# okay decompiling'
|
||||
# mem_usage = __memUsage()
|
||||
print(mess, infile)
|
||||
print mess, infile
|
||||
if current_outfile:
|
||||
sys.stdout.write("%s\r" %
|
||||
status_msg(do_verify, tot_files, okay_files, failed_files, verify_failed_files))
|
||||
|
@@ -3,7 +3,7 @@
|
||||
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
||||
# Copyright (c) 1999 John Aycock
|
||||
"""
|
||||
Common uncompyle parser routines.
|
||||
Common uncompyle6 parser routines.
|
||||
"""
|
||||
|
||||
import sys
|
||||
@@ -28,13 +28,16 @@ class PythonParser(GenericASTBuilder):
|
||||
|
||||
def __init__(self, AST, start, debug):
|
||||
super(PythonParser, self).__init__(AST, start, debug)
|
||||
self.collect = [
|
||||
# FIXME: customize per python parser version
|
||||
nt_list = [
|
||||
'stmts', 'except_stmts', '_stmts', 'load_attrs',
|
||||
'exprlist', 'kvlist', 'kwargs', 'come_froms', '_come_from',
|
||||
'importlist',
|
||||
# Python < 3
|
||||
'print_items',
|
||||
# PyPy:
|
||||
'kvlist_n']
|
||||
self.collect = frozenset(nt_list)
|
||||
|
||||
def ast_first_offset(self, ast):
|
||||
if hasattr(ast, 'offset'):
|
||||
@@ -42,21 +45,25 @@ class PythonParser(GenericASTBuilder):
|
||||
else:
|
||||
return self.ast_first_offset(ast[0])
|
||||
|
||||
def add_unique_rule(self, rule, opname, count, customize):
|
||||
def add_unique_rule(self, rule, opname, arg_count, customize):
|
||||
"""Add rule to grammar, but only if it hasn't been added previously
|
||||
opname and count are used in the customize() semantic the actions
|
||||
to add the semantic action rule. Often, count is not used.
|
||||
opname and stack_count are used in the customize() semantic
|
||||
the actions to add the semantic action rule. Stack_count is
|
||||
used in custom opcodes like MAKE_FUNCTION to indicate how
|
||||
many arguments it has. Often it is not used.
|
||||
"""
|
||||
if rule not in self.new_rules:
|
||||
# print("XXX ", rule) # debug
|
||||
self.new_rules.add(rule)
|
||||
self.addRule(rule, nop_func)
|
||||
customize[opname] = count
|
||||
customize[opname] = arg_count
|
||||
pass
|
||||
return
|
||||
|
||||
def add_unique_rules(self, rules, customize):
|
||||
"""Add rules (a list of string) to grammar
|
||||
"""Add rules (a list of string) to grammar. Note that
|
||||
the rules must not be those that set arg_count in the
|
||||
custom dictionary.
|
||||
"""
|
||||
for rule in rules:
|
||||
if len(rule) == 0:
|
||||
@@ -66,7 +73,9 @@ class PythonParser(GenericASTBuilder):
|
||||
return
|
||||
|
||||
def add_unique_doc_rules(self, rules_str, customize):
|
||||
"""Add rules (a docstring-like list of rules) to grammar
|
||||
"""Add rules (a docstring-like list of rules) to grammar.
|
||||
Note that the rules must not be those that set arg_count in the
|
||||
custom dictionary.
|
||||
"""
|
||||
rules = [r.strip() for r in rules_str.split("\n")]
|
||||
self.add_unique_rules(rules, customize)
|
||||
@@ -83,17 +92,17 @@ class PythonParser(GenericASTBuilder):
|
||||
for i in dir(self):
|
||||
setattr(self, i, None)
|
||||
|
||||
def debug_reduce(self, rule, tokens, parent, i):
|
||||
def debug_reduce(self, rule, tokens, parent, last_token_pos):
|
||||
"""Customized format and print for our kind of tokens
|
||||
which gets called in debugging grammar reduce rules
|
||||
"""
|
||||
def fix(c):
|
||||
s = str(c)
|
||||
i = s.find('_')
|
||||
if i == -1:
|
||||
last_token_pos = s.find('_')
|
||||
if last_token_pos == -1:
|
||||
return s
|
||||
else:
|
||||
return s[:i]
|
||||
return s[:last_token_pos]
|
||||
|
||||
prefix = ''
|
||||
if parent and tokens:
|
||||
@@ -105,34 +114,38 @@ class PythonParser(GenericASTBuilder):
|
||||
if hasattr(p_token, 'offset'):
|
||||
prefix += "%3s" % fix(p_token.offset)
|
||||
if len(rule[1]) > 1:
|
||||
prefix += '-%-3s ' % fix(tokens[i-1].offset)
|
||||
prefix += '-%-3s ' % fix(tokens[last_token_pos-1].offset)
|
||||
else:
|
||||
prefix += ' '
|
||||
else:
|
||||
prefix = ' '
|
||||
|
||||
print("%s%s ::= %s" % (prefix, rule[0], ' '.join(rule[1])))
|
||||
print("%s%s ::= %s (%d)" % (prefix, rule[0], ' '.join(rule[1]), last_token_pos))
|
||||
|
||||
def error(self, instructions, index):
|
||||
# Find the last line boundary
|
||||
start, finish = -1, -1
|
||||
for start in range(index, -1, -1):
|
||||
if instructions[start].linestart: break
|
||||
pass
|
||||
for finish in range(index+1, len(instructions)):
|
||||
if instructions[finish].linestart: break
|
||||
pass
|
||||
err_token = instructions[index]
|
||||
print("Instruction context:")
|
||||
for i in range(start, finish):
|
||||
if i != index:
|
||||
indent = ' '
|
||||
else:
|
||||
indent = '-> '
|
||||
print("%s%s" % (indent, instructions[i]))
|
||||
raise ParserError(err_token, err_token.offset)
|
||||
if start > 0:
|
||||
err_token = instructions[index]
|
||||
print("Instruction context:")
|
||||
for i in range(start, finish):
|
||||
if i != index:
|
||||
indent = ' '
|
||||
else:
|
||||
indent = '-> '
|
||||
print "%s%s" % (indent, instructions[i])
|
||||
raise ParserError(err_token, err_token.offset)
|
||||
else:
|
||||
raise ParserError(None, -1)
|
||||
|
||||
def typestring(self, token):
|
||||
return token.type
|
||||
return token.kind
|
||||
|
||||
def nonterminal(self, nt, args):
|
||||
if nt in self.collect and len(args) > 1:
|
||||
@@ -254,8 +267,15 @@ class PythonParser(GenericASTBuilder):
|
||||
|
||||
stmt ::= return_stmt
|
||||
return_stmt ::= ret_expr RETURN_VALUE
|
||||
return_stmt_lambda ::= ret_expr RETURN_VALUE_LAMBDA
|
||||
|
||||
# return_stmts are a sequence of statements that ends in a RETURN statement.
|
||||
# In later Python versions with jump optimization, this can cause JUMPs
|
||||
# that would normally appear to be omitted.
|
||||
|
||||
return_stmts ::= return_stmt
|
||||
return_stmts ::= _stmts return_stmt
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -389,15 +409,15 @@ class PythonParser(GenericASTBuilder):
|
||||
stmt ::= importstar
|
||||
stmt ::= importmultiple
|
||||
|
||||
importlist2 ::= importlist2 import_as
|
||||
importlist2 ::= import_as
|
||||
import_as ::= IMPORT_NAME designator
|
||||
import_as ::= IMPORT_NAME load_attrs designator
|
||||
import_as ::= IMPORT_FROM designator
|
||||
importlist ::= importlist import_as
|
||||
importlist ::= import_as
|
||||
import_as ::= IMPORT_NAME designator
|
||||
import_as ::= IMPORT_NAME load_attrs designator
|
||||
import_as ::= IMPORT_FROM designator
|
||||
|
||||
importstmt ::= LOAD_CONST LOAD_CONST import_as
|
||||
importstar ::= LOAD_CONST LOAD_CONST IMPORT_NAME IMPORT_STAR
|
||||
importfrom ::= LOAD_CONST LOAD_CONST IMPORT_NAME importlist2 POP_TOP
|
||||
importfrom ::= LOAD_CONST LOAD_CONST IMPORT_NAME importlist POP_TOP
|
||||
importmultiple ::= LOAD_CONST LOAD_CONST import_as imports_cont
|
||||
|
||||
imports_cont ::= imports_cont import_cont
|
||||
@@ -469,27 +489,24 @@ class PythonParser(GenericASTBuilder):
|
||||
expr ::= buildslice3
|
||||
expr ::= yield
|
||||
|
||||
# Possibly Python < 2.3
|
||||
# expr ::= SET_LINENO
|
||||
|
||||
binary_expr ::= expr expr binary_op
|
||||
binary_op ::= BINARY_ADD
|
||||
binary_op ::= BINARY_MULTIPLY
|
||||
binary_op ::= BINARY_AND
|
||||
binary_op ::= BINARY_OR
|
||||
binary_op ::= BINARY_XOR
|
||||
binary_op ::= BINARY_SUBTRACT
|
||||
binary_op ::= BINARY_TRUE_DIVIDE
|
||||
binary_op ::= BINARY_FLOOR_DIVIDE
|
||||
binary_op ::= BINARY_MODULO
|
||||
binary_op ::= BINARY_LSHIFT
|
||||
binary_op ::= BINARY_RSHIFT
|
||||
binary_op ::= BINARY_POWER
|
||||
binary_op ::= BINARY_ADD
|
||||
binary_op ::= BINARY_MULTIPLY
|
||||
binary_op ::= BINARY_AND
|
||||
binary_op ::= BINARY_OR
|
||||
binary_op ::= BINARY_XOR
|
||||
binary_op ::= BINARY_SUBTRACT
|
||||
binary_op ::= BINARY_TRUE_DIVIDE
|
||||
binary_op ::= BINARY_FLOOR_DIVIDE
|
||||
binary_op ::= BINARY_MODULO
|
||||
binary_op ::= BINARY_LSHIFT
|
||||
binary_op ::= BINARY_RSHIFT
|
||||
binary_op ::= BINARY_POWER
|
||||
|
||||
unary_expr ::= expr unary_op
|
||||
unary_op ::= UNARY_POSITIVE
|
||||
unary_op ::= UNARY_NEGATIVE
|
||||
unary_op ::= UNARY_INVERT
|
||||
unary_expr ::= expr unary_op
|
||||
unary_op ::= UNARY_POSITIVE
|
||||
unary_op ::= UNARY_NEGATIVE
|
||||
unary_op ::= UNARY_INVERT
|
||||
|
||||
unary_not ::= expr UNARY_NOT
|
||||
|
||||
@@ -530,7 +547,9 @@ class PythonParser(GenericASTBuilder):
|
||||
stmt ::= return_lambda
|
||||
stmt ::= conditional_lambda
|
||||
|
||||
return_lambda ::= ret_expr RETURN_VALUE LAMBDA_MARKER
|
||||
return_lambda ::= ret_expr RETURN_VALUE_LAMBDA LAMBDA_MARKER
|
||||
return_lambda ::= ret_expr RETURN_VALUE_LAMBDA
|
||||
|
||||
conditional_lambda ::= expr jmp_false return_if_stmt return_stmt LAMBDA_MARKER
|
||||
|
||||
cmp ::= cmp_list
|
||||
@@ -726,7 +745,7 @@ def get_python_parser(
|
||||
else:
|
||||
p = parse3.Python3ParserSingle(debug_parser)
|
||||
p.version = version
|
||||
# p.dumpGrammar() # debug
|
||||
# p.dump_grammar() # debug
|
||||
return p
|
||||
|
||||
class PythonParserSingle(PythonParser):
|
||||
@@ -774,4 +793,4 @@ if __name__ == '__main__':
|
||||
ast = python_parser(PYTHON_VERSION, co, showasm=True, is_pypy=IS_PYPY)
|
||||
print(ast)
|
||||
return
|
||||
parse_test(parse_test.__code__)
|
||||
# parse_test(parse_test.__code__)
|
||||
|
@@ -16,7 +16,7 @@ class AST(spark_AST):
|
||||
return self.__repr1__('', None)
|
||||
|
||||
def __repr1__(self, indent, sibNum=None):
|
||||
rv = str(self.type)
|
||||
rv = str(self.kind)
|
||||
if sibNum is not None:
|
||||
rv = "%2d. %s" % (sibNum, rv)
|
||||
enumerate_children = False
|
||||
|
@@ -29,8 +29,8 @@ class Python15ParserSingle(Python21Parser, PythonParserSingle):
|
||||
if __name__ == '__main__':
|
||||
# Check grammar
|
||||
p = Python15Parser()
|
||||
p.checkGrammar()
|
||||
p.dumpGrammar()
|
||||
p.check_grammar()
|
||||
p.dump_grammar()
|
||||
|
||||
# local variables:
|
||||
# tab-width: 4
|
||||
|
@@ -395,6 +395,8 @@ class Python2Parser(PythonParser):
|
||||
return
|
||||
|
||||
def reduce_is_invalid(self, rule, ast, tokens, first, last):
|
||||
if tokens is None:
|
||||
return False
|
||||
lhs = rule[0]
|
||||
if lhs in ('augassign1', 'augassign2') and ast[0][0] == 'and':
|
||||
return True
|
||||
@@ -415,4 +417,4 @@ class Python2ParserSingle(Python2Parser, PythonParserSingle):
|
||||
if __name__ == '__main__':
|
||||
# Check grammar
|
||||
p = Python2Parser()
|
||||
p.checkGrammar()
|
||||
p.check_grammar()
|
||||
|
@@ -33,8 +33,8 @@ class Python21ParserSingle(Python22Parser, PythonParserSingle):
|
||||
if __name__ == '__main__':
|
||||
# Check grammar
|
||||
p = Python21Parser()
|
||||
p.checkGrammar()
|
||||
p.dumpGrammar()
|
||||
p.check_grammar()
|
||||
p.dump_grammar()
|
||||
|
||||
# local variables:
|
||||
# tab-width: 4
|
||||
|
@@ -26,8 +26,8 @@ class Python22ParserSingle(Python23Parser, PythonParserSingle):
|
||||
if __name__ == '__main__':
|
||||
# Check grammar
|
||||
p = Python22Parser()
|
||||
p.checkGrammar()
|
||||
p.dumpGrammar()
|
||||
p.check_grammar()
|
||||
p.dump_grammar()
|
||||
|
||||
# local variables:
|
||||
# tab-width: 4
|
||||
|
@@ -67,8 +67,8 @@ class Python23ParserSingle(Python23Parser, PythonParserSingle):
|
||||
if __name__ == '__main__':
|
||||
# Check grammar
|
||||
p = Python23Parser()
|
||||
p.checkGrammar()
|
||||
p.dumpGrammar()
|
||||
p.check_grammar()
|
||||
p.dump_grammar()
|
||||
|
||||
# local variables:
|
||||
# tab-width: 4
|
||||
|
@@ -27,7 +27,7 @@ class Python24Parser(Python25Parser):
|
||||
# keep positions similar to simplify semantic actions
|
||||
|
||||
importstmt ::= filler LOAD_CONST import_as
|
||||
importfrom ::= filler LOAD_CONST IMPORT_NAME importlist2 POP_TOP
|
||||
importfrom ::= filler LOAD_CONST IMPORT_NAME importlist POP_TOP
|
||||
importstar ::= filler LOAD_CONST IMPORT_NAME IMPORT_STAR
|
||||
|
||||
importmultiple ::= filler LOAD_CONST import_as imports_cont
|
||||
@@ -55,13 +55,14 @@ class Python24Parser(Python25Parser):
|
||||
invalid = super(Python24Parser,
|
||||
self).reduce_is_invalid(rule, ast,
|
||||
tokens, first, last)
|
||||
if invalid:
|
||||
if invalid or tokens is None:
|
||||
return invalid
|
||||
|
||||
# FiXME: this code never gets called...
|
||||
lhs = rule[0]
|
||||
if lhs == 'nop_stmt':
|
||||
return not int(tokens[first].pattr) == tokens[last].offset
|
||||
l = len(tokens)
|
||||
if 0 <= l < len(tokens):
|
||||
return not int(tokens[first].pattr) == tokens[last].offset
|
||||
|
||||
return False
|
||||
|
||||
@@ -71,4 +72,4 @@ class Python24ParserSingle(Python24Parser, PythonParserSingle):
|
||||
if __name__ == '__main__':
|
||||
# Check grammar
|
||||
p = Python24Parser()
|
||||
p.checkGrammar()
|
||||
p.check_grammar()
|
||||
|
@@ -60,4 +60,4 @@ class Python25ParserSingle(Python26Parser, PythonParserSingle):
|
||||
if __name__ == '__main__':
|
||||
# Check grammar
|
||||
p = Python25Parser()
|
||||
p.checkGrammar()
|
||||
p.check_grammar()
|
||||
|
@@ -247,7 +247,9 @@ class Python26Parser(Python2Parser):
|
||||
and ::= expr JUMP_IF_FALSE POP_TOP expr JUMP_IF_FALSE POP_TOP
|
||||
cmp_list ::= expr cmp_list1 ROT_TWO COME_FROM POP_TOP _come_from
|
||||
|
||||
conditional_lambda ::= expr jmp_false_then return_if_stmt return_stmt LAMBDA_MARKER
|
||||
return_if_lambda ::= RETURN_END_IF_LAMBDA POP_TOP
|
||||
conditional_lambda ::= expr jmp_false_then expr return_if_lambda
|
||||
return_stmt_lambda LAMBDA_MARKER
|
||||
"""
|
||||
|
||||
def add_custom_rules(self, tokens, customize):
|
||||
@@ -258,7 +260,7 @@ class Python26Parser(Python2Parser):
|
||||
invalid = super(Python26Parser,
|
||||
self).reduce_is_invalid(rule, ast,
|
||||
tokens, first, last)
|
||||
if invalid:
|
||||
if invalid or tokens is None:
|
||||
return invalid
|
||||
if rule == ('and', ('expr', 'jmp_false', 'expr', '\\e_come_from_opt')):
|
||||
# Test that jmp_false jumps to the end of "and"
|
||||
@@ -274,10 +276,10 @@ class Python26ParserSingle(Python2Parser, PythonParserSingle):
|
||||
if __name__ == '__main__':
|
||||
# Check grammar
|
||||
p = Python26Parser()
|
||||
p.checkGrammar()
|
||||
p.check_grammar()
|
||||
from uncompyle6 import PYTHON_VERSION, IS_PYPY
|
||||
if PYTHON_VERSION == 2.6:
|
||||
lhs, rhs, tokens, right_recursive = p.checkSets()
|
||||
lhs, rhs, tokens, right_recursive = p.check_sets()
|
||||
from uncompyle6.scanner import get_scanner
|
||||
s = get_scanner(PYTHON_VERSION, IS_PYPY)
|
||||
opcode_set = set(s.opc.opname).union(set(
|
||||
|
@@ -94,6 +94,10 @@ class Python27Parser(Python2Parser):
|
||||
WITH_CLEANUP END_FINALLY
|
||||
|
||||
# Common with 2.6
|
||||
return_if_lambda ::= RETURN_END_IF_LAMBDA COME_FROM
|
||||
conditional_lambda ::= expr jmp_false expr return_if_lambda
|
||||
return_stmt_lambda LAMBDA_MARKER
|
||||
|
||||
while1stmt ::= SETUP_LOOP return_stmts bp_come_from
|
||||
while1stmt ::= SETUP_LOOP return_stmts COME_FROM
|
||||
"""
|
||||
@@ -125,10 +129,10 @@ class Python27ParserSingle(Python27Parser, PythonParserSingle):
|
||||
if __name__ == '__main__':
|
||||
# Check grammar
|
||||
p = Python27Parser()
|
||||
p.checkGrammar()
|
||||
p.check_grammar()
|
||||
from uncompyle6 import PYTHON_VERSION, IS_PYPY
|
||||
if PYTHON_VERSION == 2.7:
|
||||
lhs, rhs, tokens, right_recursive = p.checkSets()
|
||||
lhs, rhs, tokens, right_recursive = p.check_sets()
|
||||
from uncompyle6.scanner import get_scanner
|
||||
s = get_scanner(PYTHON_VERSION, IS_PYPY)
|
||||
opcode_set = set(s.opc.opname).union(set(
|
||||
@@ -144,4 +148,5 @@ if __name__ == '__main__':
|
||||
for t in remain_tokens])
|
||||
remain_tokens = set(remain_tokens) - opcode_set
|
||||
print(remain_tokens)
|
||||
# p.dumpGrammar()
|
||||
p.check_grammar()
|
||||
p.dump_grammar()
|
||||
|
@@ -18,6 +18,7 @@ that a later phase can turn into a sequence of ASCII text.
|
||||
from uncompyle6.parser import PythonParser, PythonParserSingle, nop_func
|
||||
from uncompyle6.parsers.astnode import AST
|
||||
from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
|
||||
from xdis import PYTHON3
|
||||
|
||||
class Python3Parser(PythonParser):
|
||||
|
||||
@@ -154,8 +155,13 @@ class Python3Parser(PythonParser):
|
||||
# of missing "else" clauses. Therefore we include grammar
|
||||
# rules with and without ELSE.
|
||||
|
||||
ifelsestmt ::= testexpr c_stmts_opt JUMP_FORWARD else_suite opt_come_from_except
|
||||
ifelsestmt ::= testexpr c_stmts_opt jump_forward_else else_suite _come_from
|
||||
ifelsestmt ::= testexpr c_stmts_opt JUMP_FORWARD
|
||||
else_suite opt_come_from_except
|
||||
ifelsestmt ::= testexpr c_stmts_opt jump_forward_else
|
||||
else_suite _come_from
|
||||
|
||||
# ifelsestmt ::= testexpr c_stmts_opt jump_forward_else
|
||||
# passstmt _come_from
|
||||
|
||||
ifelsestmtc ::= testexpr c_stmts_opt JUMP_ABSOLUTE else_suitec
|
||||
ifelsestmtc ::= testexpr c_stmts_opt jump_absolute_else else_suitec
|
||||
@@ -251,8 +257,14 @@ class Python3Parser(PythonParser):
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
WITH_CLEANUP END_FINALLY
|
||||
|
||||
## FIXME: Right now we have erroneous jump targets
|
||||
## This below is probably not correct when the COME_FROM is put in the right place
|
||||
and ::= expr jmp_false expr COME_FROM
|
||||
or ::= expr jmp_true expr COME_FROM
|
||||
|
||||
# # something like the below is needed when the jump targets are fixed
|
||||
## or ::= expr JUMP_IF_TRUE_OR_POP COME_FROM expr
|
||||
## and ::= expr JUMP_IF_FALSE_OR_POP COME_FROM expr
|
||||
'''
|
||||
|
||||
def p_misc3(self, args):
|
||||
@@ -415,6 +427,13 @@ class Python3Parser(PythonParser):
|
||||
# a JUMP_ABSOLUTE with no COME_FROM
|
||||
conditional ::= expr jmp_false expr jump_absolute_else expr
|
||||
|
||||
return_if_lambda ::= RETURN_END_IF_LAMBDA
|
||||
conditional_lambda ::= expr jmp_false return_stmt_lambda
|
||||
return_stmt_lambda LAMBDA_MARKER
|
||||
conditional_lambda ::= expr jmp_false expr return_if_lambda
|
||||
return_stmt_lambda LAMBDA_MARKER
|
||||
|
||||
|
||||
expr ::= LOAD_CLASSNAME
|
||||
|
||||
# Python 3.4+
|
||||
@@ -425,7 +444,7 @@ class Python3Parser(PythonParser):
|
||||
@staticmethod
|
||||
def call_fn_name(token):
|
||||
"""Customize CALL_FUNCTION to add the number of positional arguments"""
|
||||
return '%s_%i' % (token.type, token.attr)
|
||||
return '%s_%i' % (token.kind, token.attr)
|
||||
|
||||
def custom_build_class_rule(self, opname, i, token, tokens, customize):
|
||||
'''
|
||||
@@ -441,16 +460,16 @@ class Python3Parser(PythonParser):
|
||||
# FIXME: I bet this can be simplified
|
||||
# look for next MAKE_FUNCTION
|
||||
for i in range(i+1, len(tokens)):
|
||||
if tokens[i].type.startswith('MAKE_FUNCTION'):
|
||||
if tokens[i].kind.startswith('MAKE_FUNCTION'):
|
||||
break
|
||||
elif tokens[i].type.startswith('MAKE_CLOSURE'):
|
||||
elif tokens[i].kind.startswith('MAKE_CLOSURE'):
|
||||
break
|
||||
pass
|
||||
assert i < len(tokens), "build_class needs to find MAKE_FUNCTION or MAKE_CLOSURE"
|
||||
assert tokens[i+1].type == 'LOAD_CONST', \
|
||||
assert tokens[i+1].kind == 'LOAD_CONST', \
|
||||
"build_class expecting CONST after MAKE_FUNCTION/MAKE_CLOSURE"
|
||||
for i in range(i, len(tokens)):
|
||||
if tokens[i].type == 'CALL_FUNCTION':
|
||||
if tokens[i].kind == 'CALL_FUNCTION':
|
||||
call_fn_tok = tokens[i]
|
||||
break
|
||||
assert call_fn_tok, "build_class custom rule needs to find CALL_FUNCTION"
|
||||
@@ -491,7 +510,7 @@ class Python3Parser(PythonParser):
|
||||
# Yes, this computation based on instruction name is a little bit hoaky.
|
||||
nak = ( len(opname)-len('CALL_FUNCTION') ) // 3
|
||||
|
||||
token.type = self.call_fn_name(token)
|
||||
token.kind = self.call_fn_name(token)
|
||||
uniq_param = args_kw + args_pos
|
||||
if self.version == 3.5 and opname.startswith('CALL_FUNCTION_VAR'):
|
||||
# Python 3.5 changes the stack position of *args. KW args come
|
||||
@@ -503,33 +522,33 @@ class Python3Parser(PythonParser):
|
||||
kw = ''
|
||||
rule = ('call_function ::= expr expr ' +
|
||||
('pos_arg ' * args_pos) +
|
||||
('kwarg ' * args_kw) + kw + token.type)
|
||||
self.add_unique_rule(rule, token.type, uniq_param, customize)
|
||||
('kwarg ' * args_kw) + kw + token.kind)
|
||||
self.add_unique_rule(rule, token.kind, uniq_param, customize)
|
||||
if self.version >= 3.6 and opname == 'CALL_FUNCTION_EX_KW':
|
||||
rule = ('call_function36 ::= '
|
||||
'expr build_tuple_unpack_with_call build_map_unpack_with_call '
|
||||
'CALL_FUNCTION_EX_KW_1')
|
||||
self.add_unique_rule(rule, token.type, uniq_param, customize)
|
||||
self.add_unique_rule(rule, token.kind, uniq_param, customize)
|
||||
rule = 'call_function ::= call_function36'
|
||||
else:
|
||||
rule = ('call_function ::= expr ' +
|
||||
('pos_arg ' * args_pos) +
|
||||
('kwarg ' * args_kw) +
|
||||
'expr ' * nak + token.type)
|
||||
'expr ' * nak + token.kind)
|
||||
|
||||
self.add_unique_rule(rule, token.type, uniq_param, customize)
|
||||
self.add_unique_rule(rule, token.kind, uniq_param, customize)
|
||||
if self.version >= 3.5:
|
||||
rule = ('async_call_function ::= expr ' +
|
||||
('pos_arg ' * args_pos) +
|
||||
('kwarg ' * args_kw) +
|
||||
'expr ' * nak + token.type +
|
||||
'expr ' * nak + token.kind +
|
||||
' GET_AWAITABLE LOAD_CONST YIELD_FROM')
|
||||
self.add_unique_rule(rule, token.type, uniq_param, customize)
|
||||
self.add_unique_rule('expr ::= async_call_function', token.type, uniq_param, customize)
|
||||
self.add_unique_rule(rule, token.kind, uniq_param, customize)
|
||||
self.add_unique_rule('expr ::= async_call_function', token.kind, uniq_param, customize)
|
||||
|
||||
rule = ('classdefdeco2 ::= LOAD_BUILD_CLASS mkfunc %s%s_%d'
|
||||
% (('expr ' * (args_pos-1)), opname, args_pos))
|
||||
self.add_unique_rule(rule, token.type, uniq_param, customize)
|
||||
self.add_unique_rule(rule, token.kind, uniq_param, customize)
|
||||
|
||||
def add_make_function_rule(self, rule, opname, attr, customize):
|
||||
"""Python 3.3 added a an addtional LOAD_CONST before MAKE_FUNCTION and
|
||||
@@ -606,7 +625,7 @@ class Python3Parser(PythonParser):
|
||||
call_function ::= expr CALL_METHOD
|
||||
"""
|
||||
for i, token in enumerate(tokens):
|
||||
opname = token.type
|
||||
opname = token.kind
|
||||
opname_base = opname[:opname.rfind('_')]
|
||||
|
||||
if opname == 'PyPy':
|
||||
@@ -890,8 +909,11 @@ class Python3Parser(PythonParser):
|
||||
elif lhs == 'annotate_tuple':
|
||||
return not isinstance(tokens[first].attr, tuple)
|
||||
elif lhs == 'kwarg':
|
||||
return not (isinstance(tokens[first].attr, unicode) or
|
||||
isinstance(tokens[first].attr, str))
|
||||
arg = tokens[first].attr
|
||||
if PYTHON3:
|
||||
return not isinstance(arg, str)
|
||||
else:
|
||||
return not (isinstance(arg, str) or isinstance(arg, unicode))
|
||||
elif lhs == 'while1elsestmt':
|
||||
# if SETUP_LOOP target spans the else part, then this is
|
||||
# not while1else. Also do for whileTrue?
|
||||
@@ -900,7 +922,8 @@ class Python3Parser(PythonParser):
|
||||
last += 1
|
||||
return tokens[first].attr == tokens[last].offset
|
||||
elif lhs == 'while1stmt':
|
||||
if tokens[last] in ('COME_FROM_LOOP', 'JUMP_BACK'):
|
||||
if (0 <= last < len(tokens)
|
||||
and tokens[last] in ('COME_FROM_LOOP', 'JUMP_BACK')):
|
||||
# jump_back should be right afer SETUP_LOOP. Test?
|
||||
last += 1
|
||||
while last < len(tokens) and isinstance(tokens[last].offset, str):
|
||||
@@ -944,10 +967,10 @@ def info(args):
|
||||
p = Python32Parser()
|
||||
elif arg == '3.0':
|
||||
p = Python30Parser()
|
||||
p.checkGrammar()
|
||||
p.check_grammar()
|
||||
if len(sys.argv) > 1 and sys.argv[1] == 'dump':
|
||||
print('-' * 50)
|
||||
p.dumpGrammar()
|
||||
p.dump_grammar()
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
|
@@ -42,7 +42,7 @@ class Python32Parser(Python3Parser):
|
||||
def add_custom_rules(self, tokens, customize):
|
||||
super(Python32Parser, self).add_custom_rules(tokens, customize)
|
||||
for i, token in enumerate(tokens):
|
||||
opname = token.type
|
||||
opname = token.kind
|
||||
if opname.startswith('MAKE_FUNCTION_A'):
|
||||
args_pos, args_kw, annotate_args = token.attr
|
||||
# Check that there are 2 annotated params?
|
||||
|
@@ -29,10 +29,10 @@ class Python34ParserSingle(Python34Parser, PythonParserSingle):
|
||||
if __name__ == '__main__':
|
||||
# Check grammar
|
||||
p = Python34Parser()
|
||||
p.checkGrammar()
|
||||
p.check_grammar()
|
||||
from uncompyle6 import PYTHON_VERSION, IS_PYPY
|
||||
if PYTHON_VERSION == 3.4:
|
||||
lhs, rhs, tokens, right_recursive = p.checkSets()
|
||||
lhs, rhs, tokens, right_recursive = p.check_sets()
|
||||
from uncompyle6.scanner import get_scanner
|
||||
s = get_scanner(PYTHON_VERSION, IS_PYPY)
|
||||
opcode_set = set(s.opc.opname).union(set(
|
||||
|
@@ -142,7 +142,7 @@ class Python35Parser(Python34Parser):
|
||||
def add_custom_rules(self, tokens, customize):
|
||||
super(Python35Parser, self).add_custom_rules(tokens, customize)
|
||||
for i, token in enumerate(tokens):
|
||||
opname = token.type
|
||||
opname = token.kind
|
||||
if opname == 'BUILD_MAP_UNPACK_WITH_CALL':
|
||||
nargs = token.attr % 256
|
||||
map_unpack_n = "map_unpack_%s" % nargs
|
||||
@@ -152,7 +152,7 @@ class Python35Parser(Python34Parser):
|
||||
self.add_unique_rule(rule, opname, token.attr, customize)
|
||||
call_token = tokens[i+1]
|
||||
if self.version == 3.5:
|
||||
rule = 'call_function ::= expr unmapexpr ' + call_token.type
|
||||
rule = 'call_function ::= expr unmapexpr ' + call_token.kind
|
||||
self.add_unique_rule(rule, opname, token.attr, customize)
|
||||
pass
|
||||
pass
|
||||
@@ -164,10 +164,10 @@ class Python35ParserSingle(Python35Parser, PythonParserSingle):
|
||||
if __name__ == '__main__':
|
||||
# Check grammar
|
||||
p = Python35Parser()
|
||||
p.checkGrammar()
|
||||
p.check_grammar()
|
||||
from uncompyle6 import PYTHON_VERSION, IS_PYPY
|
||||
if PYTHON_VERSION == 3.5:
|
||||
lhs, rhs, tokens, right_recursive = p.checkSets()
|
||||
lhs, rhs, tokens, right_recursive = p.check_sets()
|
||||
from uncompyle6.scanner import get_scanner
|
||||
s = get_scanner(PYTHON_VERSION, IS_PYPY)
|
||||
opcode_set = set(s.opc.opname).union(set(
|
||||
|
@@ -31,12 +31,49 @@ class Python36Parser(Python35Parser):
|
||||
|
||||
call_function ::= expr expr CALL_FUNCTION_EX
|
||||
call_function ::= expr expr expr CALL_FUNCTION_EX_KW_1
|
||||
|
||||
# This might be valid in < 3.6
|
||||
and ::= expr jmp_false expr
|
||||
|
||||
# Adds a COME_FROM_ASYNC_WITH over 3.5
|
||||
# FIXME: remove corresponding rule for 3.5?
|
||||
async_with_as_stmt ::= expr
|
||||
BEFORE_ASYNC_WITH GET_AWAITABLE LOAD_CONST YIELD_FROM
|
||||
SETUP_ASYNC_WITH designator
|
||||
suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST
|
||||
COME_FROM_ASYNC_WITH
|
||||
WITH_CLEANUP_START
|
||||
GET_AWAITABLE LOAD_CONST YIELD_FROM
|
||||
WITH_CLEANUP_FINISH END_FINALLY
|
||||
async_with_stmt ::= expr
|
||||
BEFORE_ASYNC_WITH GET_AWAITABLE LOAD_CONST YIELD_FROM
|
||||
SETUP_ASYNC_WITH POP_TOP suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST
|
||||
COME_FROM_ASYNC_WITH
|
||||
WITH_CLEANUP_START
|
||||
GET_AWAITABLE LOAD_CONST YIELD_FROM
|
||||
WITH_CLEANUP_FINISH END_FINALLY
|
||||
|
||||
except_suite ::= c_stmts_opt COME_FROM POP_EXCEPT jump_except COME_FROM
|
||||
|
||||
# In 3.6+, A sequence of statements ending in a RETURN can cause
|
||||
# JUMP_FORWARD END_FINALLY to be omitted from try middle
|
||||
|
||||
except_return ::= POP_TOP POP_TOP POP_TOP return_stmts
|
||||
try_middle ::= JUMP_FORWARD COME_FROM_EXCEPT except_return
|
||||
|
||||
# Try middle following a return_stmts
|
||||
try_middle36 ::= COME_FROM_EXCEPT except_stmts END_FINALLY
|
||||
|
||||
stmt ::= trystmt36
|
||||
trystmt36 ::= SETUP_EXCEPT return_stmts try_middle36 opt_come_from_except
|
||||
"""
|
||||
|
||||
def add_custom_rules(self, tokens, customize):
|
||||
super(Python36Parser, self).add_custom_rules(tokens, customize)
|
||||
for i, token in enumerate(tokens):
|
||||
opname = token.type
|
||||
opname = token.kind
|
||||
|
||||
if opname == 'FORMAT_VALUE':
|
||||
rules_str = """
|
||||
@@ -64,10 +101,10 @@ class Python36Parser(Python35Parser):
|
||||
|
||||
if opname.startswith('CALL_FUNCTION_KW'):
|
||||
values = 'expr ' * token.attr
|
||||
rule = 'call_function ::= expr kwargs_only_36 {token.type}'.format(**locals())
|
||||
self.add_unique_rule(rule, token.type, token.attr, customize)
|
||||
rule = 'call_function ::= expr kwargs_only_36 {token.kind}'.format(**locals())
|
||||
self.add_unique_rule(rule, token.kind, token.attr, customize)
|
||||
rule = 'kwargs_only_36 ::= {values} LOAD_CONST'.format(**locals())
|
||||
self.add_unique_rule(rule, token.type, token.attr, customize)
|
||||
self.add_unique_rule(rule, token.kind, token.attr, customize)
|
||||
else:
|
||||
super(Python36Parser, self).custom_classfunc_rule(opname, token, customize)
|
||||
|
||||
@@ -78,10 +115,10 @@ class Python36ParserSingle(Python36Parser, PythonParserSingle):
|
||||
if __name__ == '__main__':
|
||||
# Check grammar
|
||||
p = Python36Parser()
|
||||
p.checkGrammar()
|
||||
p.check_grammar()
|
||||
from uncompyle6 import PYTHON_VERSION, IS_PYPY
|
||||
if PYTHON_VERSION == 3.6:
|
||||
lhs, rhs, tokens, right_recursive = p.checkSets()
|
||||
lhs, rhs, tokens, right_recursive = p.check_sets()
|
||||
from uncompyle6.scanner import get_scanner
|
||||
s = get_scanner(PYTHON_VERSION, IS_PYPY)
|
||||
opcode_set = set(s.opc.opname).union(set(
|
||||
|
@@ -2,7 +2,6 @@
|
||||
"""
|
||||
spark grammar differences over Python 3.6 for Python 3.7
|
||||
"""
|
||||
from __future__ import print_function
|
||||
|
||||
from uncompyle6.parser import PythonParserSingle
|
||||
from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
|
||||
@@ -21,10 +20,10 @@ class Python37ParserSingle(Python37Parser, PythonParserSingle):
|
||||
if __name__ == '__main__':
|
||||
# Check grammar
|
||||
p = Python37Parser()
|
||||
p.checkGrammar()
|
||||
p.check_grammar()
|
||||
from uncompyle6 import PYTHON_VERSION, IS_PYPY
|
||||
if PYTHON_VERSION == 3.7:
|
||||
lhs, rhs, tokens, right_recursive = p.checkSets()
|
||||
lhs, rhs, tokens, right_recursive = p.check_sets()
|
||||
from uncompyle6.scanner import get_scanner
|
||||
s = get_scanner(PYTHON_VERSION, IS_PYPY)
|
||||
opcode_set = set(s.opc.opname).union(set(
|
||||
|
@@ -16,11 +16,12 @@ from uncompyle6 import PYTHON3, IS_PYPY
|
||||
from uncompyle6.scanners.tok import Token
|
||||
from xdis.bytecode import op_size
|
||||
from xdis.magics import py_str2float
|
||||
from xdis.util import code2num
|
||||
|
||||
# The byte code versions we support
|
||||
PYTHON_VERSIONS = (1.5,
|
||||
2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7,
|
||||
3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6)
|
||||
3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7)
|
||||
|
||||
# FIXME: DRY
|
||||
if PYTHON3:
|
||||
@@ -54,7 +55,7 @@ class Scanner(object):
|
||||
|
||||
if version in PYTHON_VERSIONS:
|
||||
if is_pypy:
|
||||
v_str = "opcode_pypy%s" % (int(version * 10))
|
||||
v_str = "opcode_%spypy" % (int(version * 10))
|
||||
else:
|
||||
v_str = "opcode_%s" % (int(version * 10))
|
||||
exec("from xdis.opcodes import %s" % v_str)
|
||||
@@ -63,6 +64,7 @@ class Scanner(object):
|
||||
raise TypeError("%s is not a Python version I know about" % version)
|
||||
|
||||
self.opname = self.opc.opname
|
||||
|
||||
# FIXME: This weird Python2 behavior is not Python3
|
||||
self.resetTokenClass()
|
||||
|
||||
@@ -82,7 +84,8 @@ class Scanner(object):
|
||||
return True
|
||||
if self.code[offset] != self.opc.JUMP_ABSOLUTE:
|
||||
return False
|
||||
return offset < self.get_target(offset)
|
||||
# FIXME 0 isn't always correct
|
||||
return offset < self.get_target(offset, 0)
|
||||
|
||||
def get_target(self, pos, op=None):
|
||||
if op is None:
|
||||
@@ -92,6 +95,10 @@ class Scanner(object):
|
||||
target += pos + 3
|
||||
return target
|
||||
|
||||
# FIXME: the below can be removed after xdis version 3.6.1 has been released
|
||||
def extended_arg_val(self, val):
|
||||
return val << self.opc.EXTENDED_ARG_SHIFT
|
||||
|
||||
def get_argument(self, pos):
|
||||
arg = self.code[pos+1] + self.code[pos+2] * 256
|
||||
return arg
|
||||
@@ -99,7 +106,7 @@ class Scanner(object):
|
||||
def print_bytecode(self):
|
||||
for i in self.op_range(0, len(self.code)):
|
||||
op = self.code[i]
|
||||
if op in self.JUMP_OPs:
|
||||
if op in self.JUMP_OPS:
|
||||
dest = self.get_target(i, op)
|
||||
print('%i\t%s\t%i' % (i, self.opname[op], dest))
|
||||
else:
|
||||
@@ -166,13 +173,20 @@ class Scanner(object):
|
||||
|
||||
result_offset = None
|
||||
current_distance = len(code)
|
||||
extended_arg = 0
|
||||
for offset in self.op_range(start, end):
|
||||
op = code[offset]
|
||||
|
||||
if op == self.opc.EXTENDED_ARG:
|
||||
arg = code2num(code, offset+1) | extended_arg
|
||||
extended_arg = self.extended_arg_val(arg)
|
||||
continue
|
||||
|
||||
if op in instr:
|
||||
if target is None:
|
||||
result_offset = offset
|
||||
else:
|
||||
dest = self.get_target(offset)
|
||||
dest = self.get_target(offset, extended_arg)
|
||||
if dest == target:
|
||||
current_distance = 0
|
||||
result_offset = offset
|
||||
@@ -201,17 +215,31 @@ class Scanner(object):
|
||||
instr = [instr]
|
||||
|
||||
result = []
|
||||
extended_arg = 0
|
||||
for offset in self.op_range(start, end):
|
||||
|
||||
op = code[offset]
|
||||
|
||||
if op == self.opc.EXTENDED_ARG:
|
||||
arg = code2num(code, offset+1) | extended_arg
|
||||
extended_arg = self.extended_arg_val(arg)
|
||||
continue
|
||||
|
||||
if op in instr:
|
||||
if target is None:
|
||||
result.append(offset)
|
||||
else:
|
||||
t = self.get_target(offset)
|
||||
t = self.get_target(offset, extended_arg)
|
||||
if include_beyond_target and t >= target:
|
||||
result.append(offset)
|
||||
elif t == target:
|
||||
result.append(offset)
|
||||
pass
|
||||
pass
|
||||
pass
|
||||
extended_arg = 0
|
||||
pass
|
||||
|
||||
return result
|
||||
|
||||
def op_range(self, start, end):
|
||||
@@ -290,5 +318,6 @@ if __name__ == "__main__":
|
||||
import inspect, uncompyle6
|
||||
co = inspect.currentframe().f_code
|
||||
scanner = get_scanner('2.7.13', True)
|
||||
scanner = get_scanner(sys.version[:5], False)
|
||||
scanner = get_scanner(uncompyle6.PYTHON_VERSION, IS_PYPY, True)
|
||||
tokens, customize = scanner.ingest(co, {})
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2016 by Rocky Bernstein
|
||||
# Copyright (c) 2016-2017 by Rocky Bernstein
|
||||
"""
|
||||
Python PyPy 2.7 bytecode scanner/deparser
|
||||
|
||||
@@ -10,8 +10,8 @@ information for later use in deparsing.
|
||||
import uncompyle6.scanners.scanner27 as scan
|
||||
|
||||
# bytecode verification, verify(), uses JUMP_OPs from here
|
||||
from xdis.opcodes import opcode_pypy27
|
||||
JUMP_OPs = opcode_pypy27.JUMP_OPs
|
||||
from xdis.opcodes import opcode_27pypy
|
||||
JUMP_OPS = opcode_27pypy.JUMP_OPS
|
||||
|
||||
# We base this off of 2.6 instead of the other way around
|
||||
# because we cleaned things up this way.
|
||||
|
@@ -8,9 +8,9 @@ make things easier for decompilation.
|
||||
|
||||
import uncompyle6.scanners.scanner35 as scan
|
||||
|
||||
# bytecode verification, verify(), uses JUMP_OPs from here
|
||||
# bytecode verification, verify(), uses JUMP_OPS from here
|
||||
from xdis.opcodes import opcode_35 as opc # is this right?
|
||||
JUMP_OPs = map(lambda op: opc.opname[op], opc.hasjrel + opc.hasjabs)
|
||||
JUMP_OPs = opc.JUMP_OPS
|
||||
|
||||
# We base this off of 3.5
|
||||
class ScannerPyPy35(scan.Scanner35):
|
||||
|
@@ -11,7 +11,7 @@ import uncompyle6.scanners.scanner21 as scan
|
||||
|
||||
# bytecode verification, verify(), uses JUMP_OPs from here
|
||||
from xdis.opcodes import opcode_15
|
||||
JUMP_OPs = opcode_15.JUMP_OPs
|
||||
JUMP_OPS = opcode_15.JUMP_OPS
|
||||
|
||||
# We base this off of 2.1 instead of the other way around
|
||||
# because we cleaned things up this way.
|
||||
|
@@ -29,9 +29,9 @@ else:
|
||||
|
||||
from array import array
|
||||
|
||||
from uncompyle6.scanner import L65536
|
||||
from xdis.code import iscode
|
||||
from xdis.bytecode import op_has_argument, op_size
|
||||
from xdis.bytecode import op_has_argument, op_size, instruction_size
|
||||
from xdis.util import code2num
|
||||
|
||||
from uncompyle6.scanner import Scanner
|
||||
|
||||
@@ -97,14 +97,20 @@ class Scanner2(Scanner):
|
||||
from xdis.bytecode import Bytecode
|
||||
bytecode = Bytecode(co, self.opc)
|
||||
for instr in bytecode.get_instructions(co):
|
||||
print(instr._disassemble())
|
||||
print(instr.disassemble())
|
||||
|
||||
# Container for tokens
|
||||
# list of tokens/instructions
|
||||
tokens = []
|
||||
|
||||
# "customize" is a dict whose keys are nonterminals
|
||||
# and the value is the argument stack entries for that
|
||||
# nonterminal. The count is a little hoaky. It is mostly
|
||||
# not used, but sometimes it is.
|
||||
# "customize" is a dict whose keys are nonterminals
|
||||
customize = {}
|
||||
|
||||
if self.is_pypy:
|
||||
customize['PyPy'] = 1
|
||||
customize['PyPy'] = 0
|
||||
|
||||
Token = self.Token # shortcut
|
||||
|
||||
@@ -193,7 +199,7 @@ class Scanner2(Scanner):
|
||||
oparg = self.get_argument(offset) + extended_arg
|
||||
extended_arg = 0
|
||||
if op == self.opc.EXTENDED_ARG:
|
||||
extended_arg = oparg * L65536
|
||||
extended_arg += self.extended_arg_val(oparg)
|
||||
continue
|
||||
if op in self.opc.CONST_OPS:
|
||||
const = co.co_consts[oparg]
|
||||
@@ -485,7 +491,7 @@ class Scanner2(Scanner):
|
||||
elif op in self.setup_ops:
|
||||
count_SETUP_ += 1
|
||||
|
||||
def detect_control_flow(self, offset, op):
|
||||
def detect_control_flow(self, offset, op, extended_arg):
|
||||
"""
|
||||
Detect type of block structures and their boundaries to fix optimized jumps
|
||||
in python2.3+
|
||||
@@ -509,14 +515,13 @@ class Scanner2(Scanner):
|
||||
parent = struct
|
||||
|
||||
if op == self.opc.SETUP_LOOP:
|
||||
|
||||
# We categorize loop types: 'for', 'while', 'while 1' with
|
||||
# possibly suffixes '-loop' and '-else'
|
||||
# Try to find the jump_back instruction of the loop.
|
||||
# It could be a return instruction.
|
||||
|
||||
start = offset+3
|
||||
target = self.get_target(offset, op)
|
||||
start += instruction_size(op, self.opc)
|
||||
target = self.get_target(offset) + extended_arg
|
||||
end = self.restrict_to_parent(target, parent)
|
||||
self.setup_loop_targets[offset] = target
|
||||
self.setup_loops[target] = offset
|
||||
@@ -988,12 +993,18 @@ class Scanner2(Scanner):
|
||||
self.thens = {} # JUMP_IF's that separate the 'then' part of an 'if'
|
||||
|
||||
targets = {}
|
||||
extended_arg = 0
|
||||
for offset in self.op_range(0, n):
|
||||
op = code[offset]
|
||||
|
||||
if op == self.opc.EXTENDED_ARG:
|
||||
arg = code2num(code, offset+1) | extended_arg
|
||||
extended_arg += self.extended_arg_val(arg)
|
||||
continue
|
||||
|
||||
# Determine structures and fix jumps in Python versions
|
||||
# since 2.3
|
||||
self.detect_control_flow(offset, op)
|
||||
self.detect_control_flow(offset, op, extended_arg)
|
||||
|
||||
if op_has_argument(op, self.opc):
|
||||
label = self.fixed_jumps.get(offset)
|
||||
@@ -1045,7 +1056,9 @@ class Scanner2(Scanner):
|
||||
label = self.fixed_jumps[offset]
|
||||
targets[label] = targets.get(label, []) + [offset]
|
||||
pass
|
||||
pass
|
||||
|
||||
extended_arg = 0
|
||||
pass # for loop
|
||||
|
||||
# DEBUG:
|
||||
if debug in ('both', 'after'):
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2016 by Rocky Bernstein
|
||||
# Copyright (c) 2016-2017 by Rocky Bernstein
|
||||
"""
|
||||
Python 2.1 bytecode scanner/deparser
|
||||
|
||||
@@ -11,7 +11,7 @@ import uncompyle6.scanners.scanner22 as scan
|
||||
|
||||
# bytecode verification, verify(), uses JUMP_OPs from here
|
||||
from xdis.opcodes import opcode_21
|
||||
JUMP_OPs = opcode_21.JUMP_OPs
|
||||
JUMP_OPS = opcode_21.JUMP_OPS
|
||||
|
||||
# We base this off of 2.2 instead of the other way around
|
||||
# because we cleaned things up this way.
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2016 by Rocky Bernstein
|
||||
# Copyright (c) 2016-2017 by Rocky Bernstein
|
||||
"""
|
||||
Python 2.2 bytecode ingester.
|
||||
|
||||
@@ -11,7 +11,7 @@ import uncompyle6.scanners.scanner23 as scan
|
||||
|
||||
# bytecode verification, verify(), uses JUMP_OPs from here
|
||||
from xdis.opcodes import opcode_22
|
||||
JUMP_OPs = opcode_22.JUMP_OPs
|
||||
JUMP_OPS = opcode_22.JUMP_OPS
|
||||
|
||||
# We base this off of 2.3 instead of the other way around
|
||||
# because we cleaned things up this way.
|
||||
@@ -30,5 +30,5 @@ class Scanner22(scan.Scanner23):
|
||||
|
||||
def ingest22(self, co, classname=None, code_objects={}, show_asm=None):
|
||||
tokens, customize = self.parent_ingest(co, classname, code_objects, show_asm)
|
||||
tokens = [t for t in tokens if t.type != 'SET_LINENO']
|
||||
tokens = [t for t in tokens if t.kind != 'SET_LINENO']
|
||||
return tokens, customize
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2016 by Rocky Bernstein
|
||||
# Copyright (c) 2016-2017 by Rocky Bernstein
|
||||
"""
|
||||
Python 2.3 bytecode scanner/deparser
|
||||
|
||||
@@ -10,7 +10,7 @@ import uncompyle6.scanners.scanner24 as scan
|
||||
|
||||
# bytecode verification, verify(), uses JUMP_OPs from here
|
||||
from xdis.opcodes import opcode_23
|
||||
JUMP_OPs = opcode_23.JUMP_OPs
|
||||
JUMP_OPS = opcode_23.JUMP_OPS
|
||||
|
||||
# We base this off of 2.4 instead of the other way around
|
||||
# because we cleaned things up this way.
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2016 by Rocky Bernstein
|
||||
# Copyright (c) 2016-2017 by Rocky Bernstein
|
||||
"""
|
||||
Python 2.4 bytecode scanner/deparser
|
||||
|
||||
@@ -10,7 +10,7 @@ import uncompyle6.scanners.scanner25 as scan
|
||||
|
||||
# bytecode verification, verify(), uses JUMP_OPs from here
|
||||
from xdis.opcodes import opcode_24
|
||||
JUMP_OPs = opcode_24.JUMP_OPs
|
||||
JUMP_OPS = opcode_24.JUMP_OPS
|
||||
|
||||
# We base this off of 2.5 instead of the other way around
|
||||
# because we cleaned things up this way.
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2015-2016 by Rocky Bernstein
|
||||
# Copyright (c) 2015-2017 by Rocky Bernstein
|
||||
"""
|
||||
Python 2.5 bytecode scanner/deparser
|
||||
|
||||
@@ -11,7 +11,7 @@ import uncompyle6.scanners.scanner26 as scan
|
||||
|
||||
# bytecode verification, verify(), uses JUMP_OPs from here
|
||||
from xdis.opcodes import opcode_25
|
||||
JUMP_OPs = opcode_25.JUMP_OPs
|
||||
JUMP_OPS = opcode_25.JUMP_OPS
|
||||
|
||||
# We base this off of 2.6 instead of the other way around
|
||||
# because we cleaned things up this way.
|
||||
|
@@ -19,7 +19,7 @@ from uncompyle6.scanner import L65536
|
||||
|
||||
# bytecode verification, verify(), uses JUMP_OPs from here
|
||||
from xdis.opcodes import opcode_26
|
||||
JUMP_OPs = opcode_26.JUMP_OPs
|
||||
JUMP_OPS = opcode_26.JUMP_OPS
|
||||
|
||||
class Scanner26(scan.Scanner2):
|
||||
def __init__(self, show_asm=False):
|
||||
@@ -95,7 +95,7 @@ class Scanner26(scan.Scanner2):
|
||||
from xdis.bytecode import Bytecode
|
||||
bytecode = Bytecode(co, self.opc)
|
||||
for instr in bytecode.get_instructions(co):
|
||||
print(instr._disassemble())
|
||||
print(instr.disassemble())
|
||||
|
||||
# Container for tokens
|
||||
tokens = []
|
||||
@@ -217,8 +217,8 @@ class Scanner26(scan.Scanner2):
|
||||
# FIXME: this is a hack to catch stuff like:
|
||||
# if x: continue
|
||||
# the "continue" is not on a new line.
|
||||
if len(tokens) and tokens[-1].type == 'JUMP_BACK':
|
||||
tokens[-1].type = intern('CONTINUE')
|
||||
if len(tokens) and tokens[-1].kind == 'JUMP_BACK':
|
||||
tokens[-1].kind = intern('CONTINUE')
|
||||
|
||||
elif op in self.opc.JABS_OPS:
|
||||
pattr = repr(oparg)
|
||||
@@ -258,18 +258,18 @@ class Scanner26(scan.Scanner2):
|
||||
and self.code[offset+3] not in (self.opc.END_FINALLY,
|
||||
self.opc.POP_BLOCK)):
|
||||
if ((offset in self.linestartoffsets and
|
||||
tokens[-1].type == 'JUMP_BACK')
|
||||
tokens[-1].kind == 'JUMP_BACK')
|
||||
or offset not in self.not_continue):
|
||||
op_name = 'CONTINUE'
|
||||
else:
|
||||
# FIXME: this is a hack to catch stuff like:
|
||||
# if x: continue
|
||||
# the "continue" is not on a new line.
|
||||
if tokens[-1].type == 'JUMP_BACK':
|
||||
if tokens[-1].kind == 'JUMP_BACK':
|
||||
# We need 'intern' since we have
|
||||
# already have processed the previous
|
||||
# token.
|
||||
tokens[-1].type = intern('CONTINUE')
|
||||
tokens[-1].kind = intern('CONTINUE')
|
||||
|
||||
elif op == self.opc.LOAD_GLOBAL:
|
||||
if offset in self.load_asserts:
|
||||
|
@@ -16,7 +16,7 @@ if PYTHON3:
|
||||
|
||||
# bytecode verification, verify(), uses JUMP_OPs from here
|
||||
from xdis.opcodes import opcode_27
|
||||
JUMP_OPs = opcode_27.JUMP_OPs
|
||||
JUMP_OPS = opcode_27.JUMP_OPs
|
||||
|
||||
class Scanner27(Scanner2):
|
||||
def __init__(self, show_asm=False, is_pypy=False):
|
||||
@@ -92,9 +92,9 @@ class Scanner27(Scanner2):
|
||||
# the "continue" is not on a new line.
|
||||
n = len(tokens)
|
||||
if (n > 2 and
|
||||
tokens[-1].type == 'JUMP_BACK' and
|
||||
tokens[-1].kind == 'JUMP_BACK' and
|
||||
self.code[offset+3] == self.opc.END_FINALLY):
|
||||
tokens[-1].type = intern('CONTINUE')
|
||||
tokens[-1].kind = intern('CONTINUE')
|
||||
|
||||
pass
|
||||
|
||||
|
@@ -31,7 +31,8 @@ from array import array
|
||||
|
||||
from uncompyle6.scanner import Scanner
|
||||
from xdis.code import iscode
|
||||
from xdis.bytecode import Bytecode, op_has_argument, op_size
|
||||
from xdis.bytecode import Bytecode, instruction_size
|
||||
|
||||
from uncompyle6.scanner import Token, parse_fn_counts
|
||||
import xdis
|
||||
|
||||
@@ -143,107 +144,92 @@ class Scanner3(Scanner):
|
||||
# FIXME: remove the above in favor of:
|
||||
# self.varargs_ops = frozenset(self.opc.hasvargs)
|
||||
|
||||
def extended_arg_val(self, val):
|
||||
if self.version < 3.6:
|
||||
return val * (1<<16)
|
||||
else:
|
||||
return val * (1<<8)
|
||||
|
||||
|
||||
def ingest(self, co, classname=None, code_objects={}, show_asm=None):
|
||||
"""
|
||||
Pick out tokens from an uncompyle6 code object, and transform them,
|
||||
returning a list of uncompyle6 'Token's.
|
||||
returning a list of uncompyle6 Token's.
|
||||
|
||||
The transformations are made to assist the deparsing grammar.
|
||||
Specificially:
|
||||
- various types of LOAD_CONST's are categorized in terms of what they load
|
||||
- COME_FROM instructions are added to assist parsing control structures
|
||||
- MAKE_FUNCTION and FUNCTION_CALLS append the number of positional arguments
|
||||
- some EXTENDED_ARGS instructions are removed
|
||||
|
||||
Also, when we encounter certain tokens, we add them to a set which will cause custom
|
||||
grammar rules. Specifically, variable arg tokens like MAKE_FUNCTION or BUILD_LIST
|
||||
cause specific rules for the specific number of arguments they take.
|
||||
"""
|
||||
|
||||
# FIXME: remove this when all subsidiary functions have been removed.
|
||||
# We should be able to get everything from the self.insts list.
|
||||
self.code = array('B', co.co_code)
|
||||
|
||||
bytecode = Bytecode(co, self.opc)
|
||||
if not show_asm:
|
||||
show_asm = self.show_asm
|
||||
|
||||
# show_asm = 'after'
|
||||
# show_asm = 'both'
|
||||
if show_asm in ('both', 'before'):
|
||||
bytecode = Bytecode(co, self.opc)
|
||||
for instr in bytecode.get_instructions(co):
|
||||
print(instr._disassemble())
|
||||
print(instr.disassemble())
|
||||
|
||||
# Container for tokens
|
||||
# list of tokens/instructions
|
||||
tokens = []
|
||||
|
||||
# "customize" is a dict whose keys are nonterminals
|
||||
# and the value is the argument stack entries for that
|
||||
# nonterminal. The count is a little hoaky. It is mostly
|
||||
# not used, but sometimes it is.
|
||||
# "customize" is a dict whose keys are nonterminals
|
||||
customize = {}
|
||||
if self.is_pypy:
|
||||
customize['PyPy'] = 1
|
||||
|
||||
self.code = array('B', co.co_code)
|
||||
if self.is_pypy:
|
||||
customize['PyPy'] = 0
|
||||
|
||||
self.build_lines_data(co)
|
||||
self.build_prev_op()
|
||||
|
||||
bytecode = Bytecode(co, self.opc)
|
||||
|
||||
# FIXME: put as its own method?
|
||||
# Scan for assertions. Later we will
|
||||
# turn 'LOAD_GLOBAL' to 'LOAD_ASSERT'.
|
||||
# 'LOAD_ASSERT' is used in assert statements.
|
||||
self.load_asserts = set()
|
||||
bs = list(bytecode)
|
||||
n = len(bs)
|
||||
for i in range(n):
|
||||
inst = bs[i]
|
||||
|
||||
# We need to detect the difference between
|
||||
# "raise AssertionError" and "assert"
|
||||
self.insts = list(bytecode)
|
||||
n = len(self.insts)
|
||||
for i, inst in enumerate(self.insts):
|
||||
# We need to detect the difference between:
|
||||
# raise AssertionError
|
||||
# and
|
||||
# assert ...
|
||||
# If we have a JUMP_FORWARD after the
|
||||
# RAISE_VARARGS then we have a "raise" statement
|
||||
# else we have an "assert" statement.
|
||||
if inst.opname == 'POP_JUMP_IF_TRUE' and i+1 < n:
|
||||
next_inst = bs[i+1]
|
||||
next_inst = self.insts[i+1]
|
||||
if (next_inst.opname == 'LOAD_GLOBAL' and
|
||||
next_inst.argval == 'AssertionError'):
|
||||
for j in range(i+2, n):
|
||||
raise_inst = bs[j]
|
||||
if raise_inst.opname.startswith('RAISE_VARARGS'):
|
||||
if j+1 >= n or bs[j+1].opname != 'JUMP_FORWARD':
|
||||
self.load_asserts.add(next_inst.offset)
|
||||
pass
|
||||
break
|
||||
if (i + 2 < n and self.insts[i+2].opname.startswith('RAISE_VARARGS')):
|
||||
self.load_asserts.add(next_inst.offset)
|
||||
pass
|
||||
pass
|
||||
|
||||
# Get jump targets
|
||||
# Format: {target offset: [jump offsets]}
|
||||
jump_targets = self.find_jump_targets(show_asm)
|
||||
# print("XXX2", jump_targets)
|
||||
|
||||
last_op_was_break = False
|
||||
|
||||
extended_arg = 0
|
||||
for i, inst in enumerate(bytecode):
|
||||
|
||||
argval = inst.argval
|
||||
op = inst.opcode
|
||||
has_arg = op_has_argument(op, self.opc)
|
||||
if has_arg:
|
||||
if op == self.opc.EXTENDED_ARG:
|
||||
extended_arg += self.extended_arg_val(argval)
|
||||
|
||||
# Normally we remove EXTENDED_ARG from the
|
||||
# opcodes, but in the case of annotated functions
|
||||
# can use the EXTENDED_ARG tuple to signal we have
|
||||
# an annotated function.
|
||||
if not bs[i+1].opname.startswith("MAKE_FUNCTION"):
|
||||
continue
|
||||
|
||||
if isinstance(argval, int) and extended_arg:
|
||||
min_extended= self.extended_arg_val(1)
|
||||
if argval < min_extended:
|
||||
argval += extended_arg
|
||||
extended_arg = 0
|
||||
if op == self.opc.EXTENDED_ARG:
|
||||
# FIXME: The EXTENDED_ARG is used to signal annotation
|
||||
# parameters
|
||||
if self.insts[i+1].opcode != self.opc.MAKE_FUNCTION:
|
||||
continue
|
||||
|
||||
if inst.offset in jump_targets:
|
||||
jump_idx = 0
|
||||
@@ -262,9 +248,6 @@ class Scanner3(Scanner):
|
||||
pass
|
||||
elif inst.offset in self.except_targets:
|
||||
come_from_name = 'COME_FROM_EXCEPT_CLAUSE'
|
||||
if self.version <= 3.2:
|
||||
continue
|
||||
pass
|
||||
tokens.append(Token(come_from_name,
|
||||
None, repr(jump_offset),
|
||||
offset='%s_%s' % (inst.offset, jump_idx),
|
||||
@@ -284,10 +267,11 @@ class Scanner3(Scanner):
|
||||
pattr = inst.argrepr
|
||||
opname = inst.opname
|
||||
|
||||
if opname in ['LOAD_CONST']:
|
||||
if op in self.opc.CONST_OPS:
|
||||
const = argval
|
||||
if iscode(const):
|
||||
if const.co_name == '<lambda>':
|
||||
assert opname == 'LOAD_CONST'
|
||||
opname = 'LOAD_LAMBDA'
|
||||
elif const.co_name == '<genexpr>':
|
||||
opname = 'LOAD_GENEXPR'
|
||||
@@ -336,13 +320,13 @@ class Scanner3(Scanner):
|
||||
attr = (pos_args, name_pair_args, annotate_args)
|
||||
tokens.append(
|
||||
Token(
|
||||
type_ = opname,
|
||||
opname = opname,
|
||||
attr = attr,
|
||||
pattr = pattr,
|
||||
offset = inst.offset,
|
||||
linestart = inst.starts_line,
|
||||
op = op,
|
||||
has_arg = op_has_argument(op, op3),
|
||||
has_arg = inst.has_arg,
|
||||
opc = self.opc
|
||||
)
|
||||
)
|
||||
@@ -380,6 +364,7 @@ class Scanner3(Scanner):
|
||||
# as CONTINUE, but that's okay since we add a grammar
|
||||
# rule for that.
|
||||
pattr = argval
|
||||
# FIXME: 0 isn't always correct
|
||||
target = self.get_target(inst.offset)
|
||||
if target <= inst.offset:
|
||||
next_opname = self.opname[self.code[inst.offset+3]]
|
||||
@@ -396,31 +381,34 @@ class Scanner3(Scanner):
|
||||
# the "continue" is not on a new line.
|
||||
# There are other situations where we don't catch
|
||||
# CONTINUE as well.
|
||||
if tokens[-1].type == 'JUMP_BACK' and tokens[-1].attr <= argval:
|
||||
if tokens[-2].type == 'BREAK_LOOP':
|
||||
if tokens[-1].kind == 'JUMP_BACK' and tokens[-1].attr <= argval:
|
||||
if tokens[-2].kind == 'BREAK_LOOP':
|
||||
del tokens[-1]
|
||||
else:
|
||||
# intern is used because we are changing the *previous* token
|
||||
tokens[-1].type = intern('CONTINUE')
|
||||
tokens[-1].kind = intern('CONTINUE')
|
||||
if last_op_was_break and opname == 'CONTINUE':
|
||||
last_op_was_break = False
|
||||
continue
|
||||
|
||||
# FIXME: go over for Python 3.6+. This is sometimes wrong
|
||||
elif op == self.opc.RETURN_VALUE:
|
||||
if inst.offset in self.return_end_ifs:
|
||||
opname = 'RETURN_END_IF'
|
||||
|
||||
elif inst.offset in self.load_asserts:
|
||||
opname = 'LOAD_ASSERT'
|
||||
|
||||
last_op_was_break = opname == 'BREAK_LOOP'
|
||||
tokens.append(
|
||||
Token(
|
||||
type_ = opname,
|
||||
opname = opname,
|
||||
attr = argval,
|
||||
pattr = pattr,
|
||||
offset = inst.offset,
|
||||
linestart = inst.starts_line,
|
||||
op = op,
|
||||
has_arg = (op >= op3.HAVE_ARGUMENT),
|
||||
has_arg = inst.has_arg,
|
||||
opc = self.opc
|
||||
)
|
||||
)
|
||||
@@ -475,7 +463,7 @@ class Scanner3(Scanner):
|
||||
self.prev = self.prev_op = [0]
|
||||
for offset in self.op_range(0, codelen):
|
||||
op = code[offset]
|
||||
for _ in range(op_size(op, self.opc)):
|
||||
for _ in range(instruction_size(op, self.opc)):
|
||||
self.prev_op.append(offset)
|
||||
|
||||
def find_jump_targets(self, debug):
|
||||
@@ -511,20 +499,17 @@ class Scanner3(Scanner):
|
||||
self.setup_loops = {} # setup_loop offset given target
|
||||
|
||||
targets = {}
|
||||
for offset in self.op_range(0, n):
|
||||
op = code[offset]
|
||||
for i, inst in enumerate(self.insts):
|
||||
offset = inst.offset
|
||||
op = inst.opcode
|
||||
|
||||
# Determine structures and fix jumps in Python versions
|
||||
# since 2.3
|
||||
self.detect_control_flow(offset, targets)
|
||||
self.detect_control_flow(offset, targets, i)
|
||||
|
||||
has_arg = (op >= op3.HAVE_ARGUMENT)
|
||||
if has_arg:
|
||||
if inst.has_arg:
|
||||
label = self.fixed_jumps.get(offset)
|
||||
if self.version >= 3.6:
|
||||
oparg = code[offset+1]
|
||||
else:
|
||||
oparg = code[offset+1] + code[offset+2] * 256
|
||||
oparg = inst.arg
|
||||
next_offset = xdis.next_offset(op, self.opc, offset)
|
||||
|
||||
if label is None:
|
||||
@@ -541,7 +526,9 @@ class Scanner3(Scanner):
|
||||
label = self.fixed_jumps[offset]
|
||||
targets[label] = targets.get(label, []) + [offset]
|
||||
pass
|
||||
pass
|
||||
|
||||
pass # for loop
|
||||
|
||||
# DEBUG:
|
||||
if debug in ('both', 'after'):
|
||||
import pprint as pp
|
||||
@@ -571,7 +558,7 @@ class Scanner3(Scanner):
|
||||
if elem != code[i]:
|
||||
match = False
|
||||
break
|
||||
i += op_size(code[i], self.opc)
|
||||
i += instruction_size(code[i], self.opc)
|
||||
|
||||
if match is True:
|
||||
i = self.prev_op[i]
|
||||
@@ -597,6 +584,7 @@ class Scanner3(Scanner):
|
||||
and stmt_offset not in pass_stmts):
|
||||
# If absolute jump occurs in forward direction or it takes off from the
|
||||
# same line as previous statement, this is not a statement
|
||||
# FIXME: 0 isn't always correct
|
||||
target = self.get_target(stmt_offset)
|
||||
if target > stmt_offset or self.lines[last_stmt_offset].l_no == self.lines[stmt_offset].l_no:
|
||||
stmts.remove(stmt_offset)
|
||||
@@ -631,7 +619,7 @@ class Scanner3(Scanner):
|
||||
# Finish filling the list for last statement
|
||||
slist += [codelen] * (codelen-len(slist))
|
||||
|
||||
def get_target(self, offset):
|
||||
def get_target(self, offset, extended_arg=0):
|
||||
"""
|
||||
Get target offset for op located at given <offset>.
|
||||
"""
|
||||
@@ -648,10 +636,11 @@ class Scanner3(Scanner):
|
||||
pass
|
||||
pass
|
||||
target += rel_offset
|
||||
target += extended_arg
|
||||
|
||||
return target
|
||||
|
||||
def detect_control_flow(self, offset, targets):
|
||||
def detect_control_flow(self, offset, targets, inst_index):
|
||||
"""
|
||||
Detect structures and their boundaries to fix optimized jumps
|
||||
in python2.3+
|
||||
@@ -683,23 +672,20 @@ class Scanner3(Scanner):
|
||||
# Try to find the jump_back instruction of the loop.
|
||||
# It could be a return instruction.
|
||||
|
||||
if self.version <= 3.5:
|
||||
start = offset+3
|
||||
else:
|
||||
start = offset+2
|
||||
target = self.get_target(offset)
|
||||
start += instruction_size(op, self.opc)
|
||||
target = self.get_target(offset, 0)
|
||||
end = self.restrict_to_parent(target, parent)
|
||||
self.setup_loop_targets[offset] = target
|
||||
self.setup_loops[target] = offset
|
||||
|
||||
if target != end:
|
||||
self.fixed_jumps[offset] = end
|
||||
|
||||
(line_no, next_line_byte) = self.lines[offset]
|
||||
jump_back = self.last_instr(start, end, self.opc.JUMP_ABSOLUTE,
|
||||
next_line_byte, False)
|
||||
|
||||
if jump_back:
|
||||
jump_forward_offset = jump_back+3
|
||||
jump_forward_offset = xdis.next_offset(code[jump_back], self.opc, jump_back)
|
||||
else:
|
||||
jump_forward_offset = None
|
||||
|
||||
@@ -716,7 +702,7 @@ class Scanner3(Scanner):
|
||||
if not jump_back:
|
||||
return
|
||||
|
||||
jump_back += 2
|
||||
jump_back += 2 # FIXME ???
|
||||
if_offset = None
|
||||
if code[self.prev_op[next_line_byte]] not in self.pop_jump_tf:
|
||||
if_offset = self.prev[next_line_byte]
|
||||
@@ -726,9 +712,9 @@ class Scanner3(Scanner):
|
||||
else:
|
||||
loop_type = 'for'
|
||||
target = next_line_byte
|
||||
end = jump_back + 3
|
||||
end = xdis.next_offset(code[jump_back], self.opc, jump_back)
|
||||
else:
|
||||
if self.get_target(jump_back) >= next_line_byte:
|
||||
if self.get_target(jump_back, 0) >= next_line_byte:
|
||||
jump_back = self.last_instr(start, end, self.opc.JUMP_ABSOLUTE, start, False)
|
||||
if end > jump_back+4 and self.is_jump_forward(end):
|
||||
if self.is_jump_forward(jump_back+4):
|
||||
@@ -739,6 +725,8 @@ class Scanner3(Scanner):
|
||||
self.fixed_jumps[offset] = jump_back+4
|
||||
end = jump_back+4
|
||||
|
||||
# I think 0 right because jump_back has been adjusted for any EXTENDED_ARG
|
||||
# it encounters
|
||||
target = self.get_target(jump_back)
|
||||
|
||||
if code[target] in (self.opc.FOR_ITER, self.opc.GET_ITER):
|
||||
@@ -749,7 +737,7 @@ class Scanner3(Scanner):
|
||||
|
||||
if test == offset:
|
||||
loop_type = 'while 1'
|
||||
elif self.code[test] in op3.hasjabs+op3.hasjrel:
|
||||
elif self.code[test] in self.opc.JUMP_OPs:
|
||||
self.ignore_if.add(test)
|
||||
test_target = self.get_target(test)
|
||||
if test_target > (jump_back+3):
|
||||
@@ -757,14 +745,15 @@ class Scanner3(Scanner):
|
||||
self.not_continue.add(jump_back)
|
||||
self.loops.append(target)
|
||||
self.structs.append({'type': loop_type + '-loop',
|
||||
'start': target,
|
||||
'end': jump_back})
|
||||
if jump_back+3 != end:
|
||||
'start': target,
|
||||
'end': jump_back})
|
||||
after_jump_offset = xdis.next_offset(code[jump_back], self.opc, jump_back)
|
||||
if after_jump_offset != end:
|
||||
self.structs.append({'type': loop_type + '-else',
|
||||
'start': jump_back+3,
|
||||
'end': end})
|
||||
'start': after_jump_offset,
|
||||
'end': end})
|
||||
elif op in self.pop_jump_tf:
|
||||
start = offset + op_size(op, self.opc)
|
||||
start = offset + instruction_size(op, self.opc)
|
||||
target = self.get_target(offset)
|
||||
rtarget = self.restrict_to_parent(target, parent)
|
||||
prev_op = self.prev_op
|
||||
@@ -781,13 +770,17 @@ class Scanner3(Scanner):
|
||||
|
||||
if ((code[prev_op[target]] in self.pop_jump_if_pop) and
|
||||
(target > offset) and prev_op[target] != offset):
|
||||
# FIXME: this is not accurate The commented out below
|
||||
# is what it should be. However grammar rules right now
|
||||
# assume the incorrect offsets.
|
||||
# self.fixed_jumps[offset] = target
|
||||
self.fixed_jumps[offset] = prev_op[target]
|
||||
self.structs.append({'type': 'and/or',
|
||||
'start': start,
|
||||
'end': prev_op[target]})
|
||||
return
|
||||
|
||||
# The op offset just before the target jump offset is important
|
||||
# The opcode *two* instructions before the target jump offset is important
|
||||
# in making a determination of what we have. Save that.
|
||||
pre_rtarget = prev_op[rtarget]
|
||||
|
||||
@@ -885,7 +878,7 @@ class Scanner3(Scanner):
|
||||
# like whether the target is "END_FINALLY"
|
||||
# or if the condition jump is to a forward location
|
||||
if self.is_jump_forward(pre_rtarget):
|
||||
if_end = self.get_target(pre_rtarget)
|
||||
if_end = self.get_target(pre_rtarget, 0)
|
||||
|
||||
# If the jump target is back, we are looping
|
||||
if (if_end < pre_rtarget and
|
||||
@@ -912,7 +905,7 @@ class Scanner3(Scanner):
|
||||
'start': rtarget,
|
||||
'end': end})
|
||||
self.else_start[rtarget] = end
|
||||
elif self.is_jump_back(pre_rtarget):
|
||||
elif self.is_jump_back(pre_rtarget, 0):
|
||||
if_end = rtarget
|
||||
self.structs.append({'type': 'if-then',
|
||||
'start': start,
|
||||
@@ -939,9 +932,9 @@ class Scanner3(Scanner):
|
||||
# not from SETUP_EXCEPT
|
||||
next_op = rtarget
|
||||
if code[next_op] == self.opc.POP_BLOCK:
|
||||
next_op += op_size(self.code[next_op], self.opc)
|
||||
next_op += instruction_size(self.code[next_op], self.opc)
|
||||
if code[next_op] == self.opc.JUMP_ABSOLUTE:
|
||||
next_op += op_size(self.code[next_op], self.opc)
|
||||
next_op += instruction_size(self.code[next_op], self.opc)
|
||||
if next_op in targets:
|
||||
for try_op in targets[next_op]:
|
||||
come_from_op = code[try_op]
|
||||
@@ -949,15 +942,26 @@ class Scanner3(Scanner):
|
||||
return
|
||||
pass
|
||||
pass
|
||||
if code[pre_rtarget] == self.opc.RETURN_VALUE and self.version < 3.5:
|
||||
self.return_end_ifs.add(pre_rtarget)
|
||||
if code[pre_rtarget] == self.opc.RETURN_VALUE:
|
||||
# If we are at some sort of POP_JUMP_IF and the instruction before was
|
||||
# COMPARE_OP exception-match, then pre_rtarget is not an end_if
|
||||
if not (inst_index > 0 and self.insts[inst_index-1].argval == 'exception-match'):
|
||||
self.return_end_ifs.add(pre_rtarget)
|
||||
else:
|
||||
self.fixed_jumps[offset] = rtarget
|
||||
self.not_continue.add(pre_rtarget)
|
||||
else:
|
||||
# For now, we'll only tag forward jump.
|
||||
if rtarget > offset:
|
||||
self.fixed_jumps[offset] = rtarget
|
||||
if self.version >= 3.6:
|
||||
if target > offset:
|
||||
self.fixed_jumps[offset] = target
|
||||
pass
|
||||
else:
|
||||
# FIXME: This is probably a bug in < 3.6 and we should
|
||||
# instead use the above code. But until we smoke things
|
||||
# out we'll stick with it.
|
||||
if rtarget > offset:
|
||||
self.fixed_jumps[offset] = rtarget
|
||||
|
||||
elif op == self.opc.SETUP_EXCEPT:
|
||||
target = self.get_target(offset)
|
||||
@@ -969,7 +973,7 @@ class Scanner3(Scanner):
|
||||
if target > next_offset:
|
||||
next_op = code[next_offset]
|
||||
if (self.opc.JUMP_ABSOLUTE == next_op and
|
||||
END_FINALLY != code[xdis.next_offset(next_op, self.opc, next_offset)]):
|
||||
self.opc.END_FINALLY != code[xdis.next_offset(next_op, self.opc, next_offset)]):
|
||||
self.fixed_jumps[next_offset] = target
|
||||
self.except_targets[target] = next_offset
|
||||
|
||||
@@ -992,7 +996,8 @@ class Scanner3(Scanner):
|
||||
# misclassified as RETURN_END_IF. Handle that here.
|
||||
# In RETURN_VALUE, JUMP_ABSOLUTE, RETURN_VALUE is never RETURN_END_IF
|
||||
if op == self.opc.RETURN_VALUE:
|
||||
if (offset+1 < len(code) and code[offset+1] == self.opc.JUMP_ABSOLUTE and
|
||||
next_offset = xdis.next_offset(op, self.opc, offset)
|
||||
if (next_offset < len(code) and code[next_offset] == self.opc.JUMP_ABSOLUTE and
|
||||
offset in self.return_end_ifs):
|
||||
self.return_end_ifs.remove(offset)
|
||||
pass
|
||||
@@ -1014,7 +1019,7 @@ class Scanner3(Scanner):
|
||||
pass
|
||||
return
|
||||
|
||||
def is_jump_back(self, offset):
|
||||
def is_jump_back(self, offset, extended_arg):
|
||||
"""
|
||||
Return True if the code at offset is some sort of jump back.
|
||||
That is, it is ether "JUMP_FORWARD" or an absolute jump that
|
||||
@@ -1022,7 +1027,7 @@ class Scanner3(Scanner):
|
||||
"""
|
||||
if self.code[offset] != self.opc.JUMP_ABSOLUTE:
|
||||
return False
|
||||
return offset > self.get_target(offset)
|
||||
return offset > self.get_target(offset, extended_arg)
|
||||
|
||||
def next_except_jump(self, start):
|
||||
"""
|
||||
@@ -1044,9 +1049,9 @@ class Scanner3(Scanner):
|
||||
op = self.code[i]
|
||||
if op == self.opc.END_FINALLY:
|
||||
if count_END_FINALLY == count_SETUP_:
|
||||
assert self.code[self.prev_op[i]] in (JUMP_ABSOLUTE,
|
||||
JUMP_FORWARD,
|
||||
RETURN_VALUE)
|
||||
assert self.code[self.prev_op[i]] in frozenset([self.opc.JUMP_ABSOLUTE,
|
||||
self.opc.JUMP_FORWARD,
|
||||
self.opc.RETURN_VALUE])
|
||||
self.not_continue.add(self.prev_op[i])
|
||||
return self.prev_op[i]
|
||||
count_END_FINALLY += 1
|
||||
@@ -1064,7 +1069,11 @@ class Scanner3(Scanner):
|
||||
# Find all offsets of requested instructions
|
||||
instr_offsets = self.all_instr(start, end, instr, target, include_beyond_target)
|
||||
# Get all POP_JUMP_IF_TRUE (or) offsets
|
||||
pjit_offsets = self.all_instr(start, end, self.opc.POP_JUMP_IF_TRUE)
|
||||
if self.version == 3.0:
|
||||
jump_true_op = self.opc.JUMP_IF_TRUE
|
||||
else:
|
||||
jump_true_op = self.opc.POP_JUMP_IF_TRUE
|
||||
pjit_offsets = self.all_instr(start, end, jump_true_op)
|
||||
filtered = []
|
||||
for pjit_offset in pjit_offsets:
|
||||
pjit_tgt = self.get_target(pjit_offset) - 3
|
||||
|
@@ -8,9 +8,8 @@ scanner routine for Python 3.
|
||||
|
||||
# bytecode verification, verify(), uses JUMP_OPs from here
|
||||
from xdis.opcodes import opcode_30 as opc
|
||||
from xdis.bytecode import op_size
|
||||
|
||||
JUMP_OPs = map(lambda op: opc.opname[op], opc.hasjrel + opc.hasjabs)
|
||||
from xdis.bytecode import instruction_size, next_offset
|
||||
import xdis
|
||||
|
||||
JUMP_TF = frozenset([opc.JUMP_IF_FALSE, opc.JUMP_IF_TRUE])
|
||||
|
||||
@@ -22,7 +21,7 @@ class Scanner30(Scanner3):
|
||||
return
|
||||
pass
|
||||
|
||||
def detect_control_flow(self, offset, targets):
|
||||
def detect_control_flow(self, offset, targets, inst_index):
|
||||
"""
|
||||
Detect structures and their boundaries to fix optimized jumps
|
||||
Python 3.0 is more like Python 2.6 than it is Python 3.x.
|
||||
@@ -53,7 +52,7 @@ class Scanner30(Scanner3):
|
||||
# Try to find the jump_back instruction of the loop.
|
||||
# It could be a return instruction.
|
||||
|
||||
start = offset+3
|
||||
start += instruction_size(op, self.opc)
|
||||
target = self.get_target(offset)
|
||||
end = self.restrict_to_parent(target, parent)
|
||||
self.setup_loop_targets[offset] = target
|
||||
@@ -67,7 +66,7 @@ class Scanner30(Scanner3):
|
||||
next_line_byte, False)
|
||||
|
||||
if jump_back:
|
||||
jump_forward_offset = jump_back+3
|
||||
jump_forward_offset = next_offset(code[jump_back], self.opc, jump_back)
|
||||
else:
|
||||
jump_forward_offset = None
|
||||
|
||||
@@ -97,7 +96,7 @@ class Scanner30(Scanner3):
|
||||
target = next_line_byte
|
||||
end = jump_back + 3
|
||||
else:
|
||||
if self.get_target(jump_back) >= next_line_byte:
|
||||
if self.get_target(jump_back, 0) >= next_line_byte:
|
||||
jump_back = self.last_instr(start, end, self.opc.JUMP_ABSOLUTE, start, False)
|
||||
if end > jump_back+4 and self.is_jump_forward(end):
|
||||
if self.is_jump_forward(jump_back+4):
|
||||
@@ -108,7 +107,7 @@ class Scanner30(Scanner3):
|
||||
self.fixed_jumps[offset] = jump_back+4
|
||||
end = jump_back+4
|
||||
|
||||
target = self.get_target(jump_back)
|
||||
target = self.get_target(jump_back, 0)
|
||||
|
||||
if code[target] in (self.opc.FOR_ITER, self.opc.GET_ITER):
|
||||
loop_type = 'for'
|
||||
@@ -118,7 +117,7 @@ class Scanner30(Scanner3):
|
||||
|
||||
if test == offset:
|
||||
loop_type = 'while 1'
|
||||
elif self.code[test] in opc.JUMP_OPs:
|
||||
elif self.code[test] in self.opc.JUMP_OPs:
|
||||
self.ignore_if.add(test)
|
||||
test_target = self.get_target(test)
|
||||
if test_target > (jump_back+3):
|
||||
@@ -126,14 +125,15 @@ class Scanner30(Scanner3):
|
||||
self.not_continue.add(jump_back)
|
||||
self.loops.append(target)
|
||||
self.structs.append({'type': loop_type + '-loop',
|
||||
'start': target,
|
||||
'end': jump_back})
|
||||
if jump_back+3 != end:
|
||||
'start': target,
|
||||
'end': jump_back})
|
||||
after_jump_offset = xdis.next_offset(code[jump_back], self.opc, jump_back)
|
||||
if after_jump_offset != end:
|
||||
self.structs.append({'type': loop_type + '-else',
|
||||
'start': jump_back+3,
|
||||
'end': end})
|
||||
elif op in JUMP_TF:
|
||||
start = offset + op_size(op, self.opc)
|
||||
'start': after_jump_offset,
|
||||
'end': end})
|
||||
elif op in self.pop_jump_tf:
|
||||
start = offset + instruction_size(op, self.opc)
|
||||
target = self.get_target(offset)
|
||||
rtarget = self.restrict_to_parent(target, parent)
|
||||
prev_op = self.prev_op
|
||||
@@ -254,7 +254,7 @@ class Scanner30(Scanner3):
|
||||
# like whether the target is "END_FINALLY"
|
||||
# or if the condition jump is to a forward location
|
||||
if self.is_jump_forward(pre_rtarget):
|
||||
if_end = self.get_target(pre_rtarget)
|
||||
if_end = self.get_target(pre_rtarget, 0)
|
||||
|
||||
# If the jump target is back, we are looping
|
||||
if (if_end < pre_rtarget and
|
||||
@@ -278,7 +278,7 @@ class Scanner30(Scanner3):
|
||||
# 'start': rtarget,
|
||||
# 'end': end})
|
||||
# self.else_start[rtarget] = end
|
||||
elif self.is_jump_back(pre_rtarget):
|
||||
elif self.is_jump_back(pre_rtarget, 0):
|
||||
if_end = rtarget
|
||||
self.structs.append({'type': 'if-then',
|
||||
'start': start,
|
||||
@@ -305,9 +305,9 @@ class Scanner30(Scanner3):
|
||||
# not from SETUP_EXCEPT
|
||||
next_op = rtarget
|
||||
if code[next_op] == self.opc.POP_BLOCK:
|
||||
next_op += op_size(self.code[next_op], self.opc)
|
||||
next_op += instruction_size(self.code[next_op], self.opc)
|
||||
if code[next_op] == self.opc.JUMP_ABSOLUTE:
|
||||
next_op += op_size(self.code[next_op], self.opc)
|
||||
next_op += instruction_size(self.code[next_op], self.opc)
|
||||
if next_op in targets:
|
||||
for try_op in targets[next_op]:
|
||||
come_from_op = code[try_op]
|
||||
@@ -367,28 +367,6 @@ class Scanner30(Scanner3):
|
||||
pass
|
||||
return
|
||||
|
||||
def rem_or(self, start, end, instr, target=None, include_beyond_target=False):
|
||||
"""
|
||||
Find offsets of all requested <instr> between <start> and <end>,
|
||||
optionally <target>ing specified offset, and return list found
|
||||
<instr> offsets which are not within any POP_JUMP_IF_TRUE jumps.
|
||||
"""
|
||||
assert(start>=0 and end<=len(self.code) and start <= end)
|
||||
|
||||
# Find all offsets of requested instructions
|
||||
instr_offsets = self.all_instr(start, end, instr, target, include_beyond_target)
|
||||
# Get all JUMP_IF_TRUE (or) offsets
|
||||
pjit_offsets = self.all_instr(start, end, opc.JUMP_IF_TRUE)
|
||||
filtered = []
|
||||
for pjit_offset in pjit_offsets:
|
||||
pjit_tgt = self.get_target(pjit_offset) - 3
|
||||
for instr_offset in instr_offsets:
|
||||
if instr_offset <= pjit_offset or instr_offset >= pjit_tgt:
|
||||
filtered.append(instr_offset)
|
||||
instr_offsets = filtered
|
||||
filtered = []
|
||||
return instr_offsets
|
||||
|
||||
if __name__ == "__main__":
|
||||
from uncompyle6 import PYTHON_VERSION
|
||||
if PYTHON_VERSION == 3.0:
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2016 by Rocky Bernstein
|
||||
# Copyright (c) 2016-2017 by Rocky Bernstein
|
||||
"""
|
||||
Python 3.1 bytecode scanner/deparser
|
||||
|
||||
@@ -8,7 +8,7 @@ scanner routine for Python 3.
|
||||
|
||||
# bytecode verification, verify(), uses JUMP_OPs from here
|
||||
from xdis.opcodes import opcode_31 as opc
|
||||
JUMP_OPs = map(lambda op: opc.opname[op], opc.hasjrel + opc.hasjabs)
|
||||
JUMP_OPS = opc.JUMP_OPS
|
||||
|
||||
from uncompyle6.scanners.scanner3 import Scanner3
|
||||
class Scanner31(Scanner3):
|
||||
|
@@ -11,7 +11,7 @@ scanner routine for Python 3.
|
||||
|
||||
# bytecode verification, verify(), uses JUMP_OPs from here
|
||||
from xdis.opcodes import opcode_32 as opc
|
||||
JUMP_OPs = map(lambda op: opc.opname[op], opc.hasjrel + opc.hasjabs)
|
||||
JUMP_OPS = opc.JUMP_OPS
|
||||
|
||||
from uncompyle6.scanners.scanner3 import Scanner3
|
||||
class Scanner32(Scanner3):
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2015-2016 by Rocky Bernstein
|
||||
# Copyright (c) 2015-2017 by Rocky Bernstein
|
||||
"""
|
||||
Python 3.3 bytecode scanner/deparser
|
||||
|
||||
@@ -8,7 +8,7 @@ scanner routine for Python 3.
|
||||
|
||||
# bytecode verification, verify(), uses JUMP_OPs from here
|
||||
from xdis.opcodes import opcode_33 as opc
|
||||
JUMP_OPs = map(lambda op: opc.opname[op], opc.hasjrel + opc.hasjabs)
|
||||
JUMP_OPS = opc.JUMP_OPS
|
||||
|
||||
from uncompyle6.scanners.scanner3 import Scanner3
|
||||
class Scanner33(Scanner3):
|
||||
|
@@ -12,7 +12,7 @@ scanner routine for Python 3.
|
||||
from xdis.opcodes import opcode_34 as opc
|
||||
|
||||
# bytecode verification, verify(), uses JUMP_OPs from here
|
||||
JUMP_OPs = map(lambda op: opc.opname[op], opc.hasjrel + opc.hasjabs)
|
||||
JUMP_OPS = opc.JUMP_OPS
|
||||
|
||||
|
||||
from uncompyle6.scanners.scanner3 import Scanner3
|
||||
|
@@ -13,7 +13,7 @@ from uncompyle6.scanners.scanner3 import Scanner3
|
||||
|
||||
# bytecode verification, verify(), uses JUMP_OPs from here
|
||||
from xdis.opcodes import opcode_35 as opc
|
||||
JUMP_OPs = map(lambda op: opc.opname[op], opc.hasjrel + opc.hasjabs)
|
||||
JUMP_OPS = opc.JUMP_OPS
|
||||
|
||||
class Scanner35(Scanner3):
|
||||
|
||||
|
@@ -11,9 +11,11 @@ scanner routine for Python 3.
|
||||
|
||||
from uncompyle6.scanners.scanner3 import Scanner3
|
||||
|
||||
# bytecode verification, verify(), uses JUMP_OPs from here
|
||||
import xdis
|
||||
|
||||
# bytecode verification, verify(), uses JUMP_OPS from here
|
||||
from xdis.opcodes import opcode_36 as opc
|
||||
JUMP_OPs = map(lambda op: opc.opname[op], opc.hasjrel + opc.hasjabs)
|
||||
JUMP_OPS = opc.JUMP_OPS
|
||||
|
||||
class Scanner36(Scanner3):
|
||||
|
||||
@@ -27,19 +29,17 @@ class Scanner36(Scanner3):
|
||||
# The lowest bit of flags indicates whether the
|
||||
# var-keyword argument is placed at the top of the stack
|
||||
if t.op == self.opc.CALL_FUNCTION_EX and t.attr & 1:
|
||||
t.type = 'CALL_FUNCTION_EX_KW'
|
||||
t.kind = 'CALL_FUNCTION_EX_KW'
|
||||
pass
|
||||
elif t.op == self.opc.CALL_FUNCTION_KW:
|
||||
t.type = 'CALL_FUNCTION_KW_{t.attr}'.format(**locals())
|
||||
t.kind = 'CALL_FUNCTION_KW_{t.attr}'.format(**locals())
|
||||
elif t.op == self.opc.BUILD_TUPLE_UNPACK_WITH_CALL:
|
||||
t.type = 'BUILD_TUPLE_UNPACK_WITH_CALL_%d' % t.attr
|
||||
t.kind = 'BUILD_TUPLE_UNPACK_WITH_CALL_%d' % t.attr
|
||||
elif t.op == self.opc.BUILD_MAP_UNPACK_WITH_CALL:
|
||||
t.type = 'BUILD_MAP_UNPACK_WITH_CALL_%d' % t.attr
|
||||
t.kind = 'BUILD_MAP_UNPACK_WITH_CALL_%d' % t.attr
|
||||
pass
|
||||
return tokens, customize
|
||||
|
||||
pass
|
||||
|
||||
if __name__ == "__main__":
|
||||
from uncompyle6 import PYTHON_VERSION
|
||||
if PYTHON_VERSION == 3.6:
|
||||
|
36
uncompyle6/scanners/scanner37.py
Normal file
36
uncompyle6/scanners/scanner37.py
Normal file
@@ -0,0 +1,36 @@
|
||||
# Copyright (c) 2016-2017 by Rocky Bernstein
|
||||
"""
|
||||
Python 3.7 bytecode decompiler scanner
|
||||
|
||||
Does some additional massaging of xdis-disassembled instructions to
|
||||
make things easier for decompilation.
|
||||
|
||||
This sets up opcodes Python's 3.6 and calls a generalized
|
||||
scanner routine for Python 3.
|
||||
"""
|
||||
|
||||
from uncompyle6.scanners.scanner3 import Scanner3
|
||||
|
||||
# bytecode verification, verify(), uses JUMP_OPs from here
|
||||
from xdis.opcodes import opcode_36 as opc
|
||||
JUMP_OPs = opc.JUMP_OPS
|
||||
|
||||
class Scanner37(Scanner3):
|
||||
|
||||
def __init__(self, show_asm=None):
|
||||
Scanner3.__init__(self, 3.7, show_asm)
|
||||
return
|
||||
pass
|
||||
|
||||
if __name__ == "__main__":
|
||||
from uncompyle6 import PYTHON_VERSION
|
||||
if PYTHON_VERSION == 3.7:
|
||||
import inspect
|
||||
co = inspect.currentframe().f_code
|
||||
tokens, customize = Scanner37().ingest(co)
|
||||
for t in tokens:
|
||||
print(t.format())
|
||||
pass
|
||||
else:
|
||||
print("Need to be Python 3.7 to demo; I am %s." %
|
||||
PYTHON_VERSION)
|
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2016 by Rocky Bernstein
|
||||
# Copyright (c) 2016-2017 by Rocky Bernstein
|
||||
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
||||
# Copyright (c) 1999 John Aycock
|
||||
|
||||
@@ -16,13 +16,12 @@ class Token:
|
||||
the contents of one line as output by dis.dis().
|
||||
"""
|
||||
# FIXME: match Python 3.4's terms:
|
||||
# type_ should be opname
|
||||
# linestart = starts_line
|
||||
# attr = argval
|
||||
# pattr = argrepr
|
||||
def __init__(self, type_, attr=None, pattr=None, offset=-1,
|
||||
def __init__(self, opname, attr=None, pattr=None, offset=-1,
|
||||
linestart=None, op=None, has_arg=None, opc=None):
|
||||
self.type = intern(type_)
|
||||
self.kind = intern(opname)
|
||||
self.op = op
|
||||
self.has_arg = has_arg
|
||||
self.attr = attr
|
||||
@@ -37,20 +36,20 @@ class Token:
|
||||
def __eq__(self, o):
|
||||
""" '==', but it's okay if offsets and linestarts are different"""
|
||||
if isinstance(o, Token):
|
||||
# Both are tokens: compare type and attr
|
||||
# Both are tokens: compare kind and attr
|
||||
# It's okay if offsets are different
|
||||
return (self.type == o.type) and (self.pattr == o.pattr)
|
||||
return (self.kind == o.kind) and (self.pattr == o.pattr)
|
||||
else:
|
||||
return self.type == o
|
||||
return self.kind == o
|
||||
|
||||
def __repr__(self):
|
||||
return str(self.type)
|
||||
return str(self.kind)
|
||||
|
||||
# def __str__(self):
|
||||
# pattr = self.pattr if self.pattr is not None else ''
|
||||
# prefix = '\n%3d ' % self.linestart if self.linestart else (' ' * 6)
|
||||
# return (prefix +
|
||||
# ('%9s %-18s %r' % (self.offset, self.type, pattr)))
|
||||
# ('%9s %-18s %r' % (self.offset, self.kind, pattr)))
|
||||
|
||||
def __str__(self):
|
||||
return self.format(line_prefix='')
|
||||
@@ -60,7 +59,7 @@ class Token:
|
||||
prefix = '\n%s%4d ' % (line_prefix, self.linestart)
|
||||
else:
|
||||
prefix = ' ' * (6 + len(line_prefix))
|
||||
offset_opname = '%6s %-17s' % (self.offset, self.type)
|
||||
offset_opname = '%6s %-17s' % (self.offset, self.kind)
|
||||
if not self.has_arg:
|
||||
return "%s%s" % (prefix, offset_opname)
|
||||
|
||||
@@ -84,14 +83,14 @@ class Token:
|
||||
pattr = self.opc.cmp_op[self.attr]
|
||||
# And so on. See xdis/bytecode.py get_instructions_bytes
|
||||
pass
|
||||
elif re.search('_\d+$', self.type):
|
||||
elif re.search('_\d+$', self.kind):
|
||||
return "%s%s%s" % (prefix, offset_opname, argstr)
|
||||
else:
|
||||
pattr = ''
|
||||
return "%s%s%s %r" % (prefix, offset_opname, argstr, pattr)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.type)
|
||||
return hash(self.kind)
|
||||
|
||||
def __getitem__(self, i):
|
||||
raise IndexError
|
||||
|
@@ -9,16 +9,16 @@ before reduction and don't reduce when there is a problem.
|
||||
"""
|
||||
|
||||
def checker(ast, in_loop, errors):
|
||||
in_loop = in_loop or ast.type in ('while1stmt', 'whileTruestmt',
|
||||
in_loop = in_loop or ast.kind in ('while1stmt', 'whileTruestmt',
|
||||
'whilestmt', 'whileelsestmt', 'while1elsestmt',
|
||||
'for_block')
|
||||
if ast.type in ('augassign1', 'augassign2') and ast[0][0] == 'and':
|
||||
if ast.kind in ('augassign1', 'augassign2') and ast[0][0] == 'and':
|
||||
text = str(ast)
|
||||
error_text = '\n# improper augmented assigment (e.g. +=, *=, ...):\n#\t' + '\n# '.join(text.split("\n")) + '\n'
|
||||
errors.append(error_text)
|
||||
|
||||
for node in ast:
|
||||
if not in_loop and node.type in ('continue_stmt', 'break_stmt'):
|
||||
if not in_loop and node.kind in ('continue_stmt', 'break_stmt'):
|
||||
text = str(node)
|
||||
error_text = '\n# not in loop:\n#\t' + '\n# '.join(text.split("\n"))
|
||||
errors.append(error_text)
|
||||
|
@@ -1,5 +1,5 @@
|
||||
# Copyright (c) 2017 by Rocky Bernstein
|
||||
"""Constants used in pysource.py"""
|
||||
"""Constants and initial table values used in pysource.py and fragments.py"""
|
||||
|
||||
import re, sys
|
||||
from uncompyle6.parsers.astnode import AST
|
||||
@@ -57,9 +57,7 @@ INDENT_PER_LEVEL = ' ' # additional intent per pretty-print level
|
||||
|
||||
TABLE_R = {
|
||||
'STORE_ATTR': ( '%c.%[1]{pattr}', 0),
|
||||
# 'STORE_SUBSCR': ( '%c[%c]', 0, 1 ),
|
||||
'DELETE_ATTR': ( '%|del %c.%[-1]{pattr}\n', 0 ),
|
||||
# 'EXEC_STMT': ( '%|exec %c in %[1]C\n', 0, (0,maxint,', ') ),
|
||||
}
|
||||
|
||||
TABLE_R0 = {
|
||||
@@ -67,151 +65,164 @@ TABLE_R0 = {
|
||||
# 'BUILD_TUPLE': ( '(%C)', (0,-1,', ') ),
|
||||
# 'CALL_FUNCTION': ( '%c(%P)', 0, (1,-1,', ') ),
|
||||
}
|
||||
|
||||
TABLE_DIRECT = {
|
||||
'BINARY_ADD': ( '+' ,),
|
||||
'BINARY_SUBTRACT': ( '-' ,),
|
||||
'BINARY_MULTIPLY': ( '*' ,),
|
||||
'BINARY_DIVIDE': ( '/' ,),
|
||||
'BINARY_ADD': ( '+' ,),
|
||||
'BINARY_SUBTRACT': ( '-' ,),
|
||||
'BINARY_MULTIPLY': ( '*' ,),
|
||||
'BINARY_DIVIDE': ( '/' ,),
|
||||
'BINARY_MATRIX_MULTIPLY': ( '@' ,),
|
||||
'BINARY_TRUE_DIVIDE': ( '/' ,), # Not in <= 2.1
|
||||
'BINARY_FLOOR_DIVIDE': ( '//' ,),
|
||||
'BINARY_MODULO': ( '%%',),
|
||||
'BINARY_POWER': ( '**',),
|
||||
'BINARY_LSHIFT': ( '<<',),
|
||||
'BINARY_RSHIFT': ( '>>',),
|
||||
'BINARY_AND': ( '&' ,),
|
||||
'BINARY_OR': ( '|' ,),
|
||||
'BINARY_XOR': ( '^' ,),
|
||||
'INPLACE_ADD': ( '+=' ,),
|
||||
'INPLACE_SUBTRACT': ( '-=' ,),
|
||||
'INPLACE_MULTIPLY': ( '*=' ,),
|
||||
'BINARY_MODULO': ( '%%',),
|
||||
'BINARY_POWER': ( '**',),
|
||||
'BINARY_LSHIFT': ( '<<',),
|
||||
'BINARY_RSHIFT': ( '>>',),
|
||||
'BINARY_AND': ( '&' ,),
|
||||
'BINARY_OR': ( '|' ,),
|
||||
'BINARY_XOR': ( '^' ,),
|
||||
'INPLACE_ADD': ( '+=' ,),
|
||||
'INPLACE_SUBTRACT': ( '-=' ,),
|
||||
'INPLACE_MULTIPLY': ( '*=' ,),
|
||||
'INPLACE_MATRIX_MULTIPLY': ( '@=' ,),
|
||||
'INPLACE_DIVIDE': ( '/=' ,),
|
||||
'INPLACE_DIVIDE': ( '/=' ,),
|
||||
'INPLACE_TRUE_DIVIDE': ( '/=' ,), # Not in <= 2.1; 2.6 generates INPLACE_DIVIDE only?
|
||||
'INPLACE_FLOOR_DIVIDE': ( '//=' ,),
|
||||
'INPLACE_MODULO': ( '%%=',),
|
||||
'INPLACE_POWER': ( '**=',),
|
||||
'INPLACE_LSHIFT': ( '<<=',),
|
||||
'INPLACE_RSHIFT': ( '>>=',),
|
||||
'INPLACE_AND': ( '&=' ,),
|
||||
'INPLACE_OR': ( '|=' ,),
|
||||
'INPLACE_XOR': ( '^=' ,),
|
||||
'binary_expr': ( '%c %c %c', 0, -1, 1 ),
|
||||
'INPLACE_MODULO': ( '%%=',),
|
||||
'INPLACE_POWER': ( '**=',),
|
||||
'INPLACE_LSHIFT': ( '<<=',),
|
||||
'INPLACE_RSHIFT': ( '>>=',),
|
||||
'INPLACE_AND': ( '&=' ,),
|
||||
'INPLACE_OR': ( '|=' ,),
|
||||
'INPLACE_XOR': ( '^=' ,),
|
||||
'binary_expr': ( '%c %c %c', 0,
|
||||
(-1, 'binary_op'),
|
||||
( 1, 'expr' ) ),
|
||||
|
||||
'UNARY_POSITIVE': ( '+',),
|
||||
'UNARY_NEGATIVE': ( '-',),
|
||||
'UNARY_INVERT': ( '~%c'),
|
||||
'unary_expr': ( '%c%c', 1, 0),
|
||||
'UNARY_POSITIVE': ( '+',),
|
||||
'UNARY_NEGATIVE': ( '-',),
|
||||
'UNARY_INVERT': ( '~'),
|
||||
'unary_expr': ( '%c%c',
|
||||
(1, 'unary_op'),
|
||||
(0, 'expr') ),
|
||||
|
||||
'unary_not': ( 'not %c', 0 ),
|
||||
'unary_convert': ( '`%c`', 0 ),
|
||||
'get_iter': ( 'iter(%c)', 0 ),
|
||||
'slice0': ( '%c[:]', 0 ),
|
||||
'slice1': ( '%c[%p:]', 0, (1, 100) ),
|
||||
'slice2': ( '%c[:%p]', 0, (1, 100) ),
|
||||
'slice3': ( '%c[%p:%p]', 0, (1, 100), (2, 100) ),
|
||||
'unary_not': ( 'not %c',
|
||||
(0, 'expr' ) ),
|
||||
'unary_convert': ( '`%c`',
|
||||
(0, 'expr' ), ),
|
||||
'get_iter': ( 'iter(%c)',
|
||||
(0, 'expr'), ),
|
||||
'slice0': ( '%c[:]',
|
||||
(0, 'expr'), ),
|
||||
'slice1': ( '%c[%p:]',
|
||||
(0, 'expr'),
|
||||
(1, 100) ),
|
||||
'slice2': ( '%c[:%p]',
|
||||
0, (1, 100) ),
|
||||
'slice3': ( '%c[%p:%p]',
|
||||
0, (1, 100), (2, 100) ),
|
||||
|
||||
'IMPORT_FROM': ( '%{pattr}', ),
|
||||
'load_attr': ( '%c.%[1]{pattr}', 0),
|
||||
'LOAD_FAST': ( '%{pattr}', ),
|
||||
'LOAD_NAME': ( '%{pattr}', ),
|
||||
'LOAD_CLASSNAME': ( '%{pattr}', ),
|
||||
'LOAD_GLOBAL': ( '%{pattr}', ),
|
||||
'LOAD_DEREF': ( '%{pattr}', ),
|
||||
'LOAD_LOCALS': ( 'locals()', ),
|
||||
'LOAD_ASSERT': ( '%{pattr}', ),
|
||||
# 'LOAD_CONST': ( '%{pattr}', ), # handled by n_LOAD_CONST
|
||||
'DELETE_FAST': ( '%|del %{pattr}\n', ),
|
||||
'DELETE_NAME': ( '%|del %{pattr}\n', ),
|
||||
'DELETE_GLOBAL': ( '%|del %{pattr}\n', ),
|
||||
'delete_subscr': ( '%|del %c[%c]\n', 0, 1,),
|
||||
'binary_subscr': ( '%c[%p]', 0, (1, 100)),
|
||||
'binary_subscr2': ( '%c[%p]', 0, (1, 100)),
|
||||
'store_subscr': ( '%c[%c]', 0, 1),
|
||||
'STORE_FAST': ( '%{pattr}', ),
|
||||
'STORE_NAME': ( '%{pattr}', ),
|
||||
'STORE_GLOBAL': ( '%{pattr}', ),
|
||||
'STORE_DEREF': ( '%{pattr}', ),
|
||||
'unpack': ( '%C%,', (1, maxint, ', ') ),
|
||||
'IMPORT_FROM': ( '%{pattr}', ),
|
||||
'load_attr': ( '%c.%[1]{pattr}', 0),
|
||||
'LOAD_FAST': ( '%{pattr}', ),
|
||||
'LOAD_NAME': ( '%{pattr}', ),
|
||||
'LOAD_CLASSNAME': ( '%{pattr}', ),
|
||||
'LOAD_GLOBAL': ( '%{pattr}', ),
|
||||
'LOAD_DEREF': ( '%{pattr}', ),
|
||||
'LOAD_LOCALS': ( 'locals()', ),
|
||||
'LOAD_ASSERT': ( '%{pattr}', ),
|
||||
'DELETE_FAST': ( '%|del %{pattr}\n', ),
|
||||
'DELETE_NAME': ( '%|del %{pattr}\n', ),
|
||||
'DELETE_GLOBAL': ( '%|del %{pattr}\n', ),
|
||||
'delete_subscr': ( '%|del %c[%c]\n', 0, 1,),
|
||||
'binary_subscr': ( '%c[%p]', 0, (1, 100)),
|
||||
'binary_subscr2': ( '%c[%p]', 0, (1, 100)),
|
||||
'store_subscr': ( '%c[%c]', 0, 1),
|
||||
'STORE_FAST': ( '%{pattr}', ),
|
||||
'STORE_NAME': ( '%{pattr}', ),
|
||||
'STORE_GLOBAL': ( '%{pattr}', ),
|
||||
'STORE_DEREF': ( '%{pattr}', ),
|
||||
'unpack': ( '%C%,', (1, maxint, ', ') ),
|
||||
|
||||
# This nonterminal we create on the fly in semantic routines
|
||||
'unpack_w_parens': ( '(%C%,)', (1, maxint, ', ') ),
|
||||
|
||||
'unpack_list': ( '[%C]', (1, maxint, ', ') ),
|
||||
'build_tuple2': ( '%P', (0, -1, ', ', 100) ),
|
||||
'unpack_list': ( '[%C]', (1, maxint, ', ') ),
|
||||
'build_tuple2': ( '%P', (0, -1, ', ', 100) ),
|
||||
|
||||
# 'list_compr': ( '[ %c ]', -2), # handled by n_list_compr
|
||||
'list_iter': ( '%c', 0),
|
||||
'list_for': ( ' for %c in %c%c', 2, 0, 3 ),
|
||||
'list_if': ( ' if %c%c', 0, 2 ),
|
||||
'list_iter': ( '%c', 0 ),
|
||||
'list_for': ( ' for %c in %c%c', 2, 0, 3 ),
|
||||
'list_if': ( ' if %c%c', 0, 2 ),
|
||||
'list_if_not': ( ' if not %p%c', (0, 22), 2 ),
|
||||
'lc_body': ( '', ), # ignore when recusing
|
||||
'lc_body': ( '', ), # ignore when recusing
|
||||
|
||||
'comp_iter': ( '%c', 0),
|
||||
'comp_if': ( ' if %c%c', 0, 2 ),
|
||||
'comp_ifnot': ( ' if not %p%c', (0, 22), 2 ),
|
||||
'comp_body': ( '', ), # ignore when recusing
|
||||
'comp_iter': ( '%c', 0 ),
|
||||
'comp_if': ( ' if %c%c', 0, 2 ),
|
||||
'comp_ifnot': ( ' if not %p%c', (0, 22), 2 ),
|
||||
'comp_body': ( '', ), # ignore when recusing
|
||||
'set_comp_body': ( '%c', 0 ),
|
||||
'gen_comp_body': ( '%c', 0 ),
|
||||
'dict_comp_body': ( '%c:%c', 1, 0 ),
|
||||
|
||||
'assign': ( '%|%c = %p\n', -1, (0, 200) ),
|
||||
'assign': ( '%|%c = %p\n', -1, (0, 200) ),
|
||||
|
||||
# The 2nd parameter should have a = suffix.
|
||||
# There is a rule with a 4th parameter "designator"
|
||||
# which we don't use here.
|
||||
'augassign1': ( '%|%c %c %c\n', 0, 2, 1),
|
||||
'augassign1': ( '%|%c %c %c\n', 0, 2, 1),
|
||||
|
||||
'augassign2': ( '%|%c.%[2]{pattr} %c %c\n', 0, -3, -4),
|
||||
'designList': ( '%c = %c', 0, -1 ),
|
||||
'augassign2': ( '%|%c.%[2]{pattr} %c %c\n', 0, -3, -4 ),
|
||||
'designList': ( '%c = %c', 0, -1 ),
|
||||
'and': ( '%c and %c', 0, 2 ),
|
||||
'ret_and': ( '%c and %c', 0, 2 ),
|
||||
'and2': ( '%c', 3 ),
|
||||
'or': ( '%c or %c', 0, 2 ),
|
||||
'ret_or': ( '%c or %c', 0, 2 ),
|
||||
'conditional': ( '%p if %p else %p', (2, 27), (0, 27), (4, 27)),
|
||||
'conditionalTrue': ( '%p if 1 else %p', (0, 27), (2, 27)),
|
||||
'ret_cond': ( '%p if %p else %p', (2, 27), (0, 27), (-1, 27)),
|
||||
'conditionalnot': ( '%p if not %p else %p', (2, 27), (0, 22), (4, 27)),
|
||||
'ret_cond_not': ( '%p if not %p else %p', (2, 27), (0, 22), (-1, 27)),
|
||||
'conditional_lambda': ( '(%c if %c else %c)', 2, 0, 3),
|
||||
'return_lambda': ('%c', 0),
|
||||
'compare': ( '%p %[-1]{pattr.replace("-", " ")} %p', (0, 19), (1, 19) ),
|
||||
'cmp_list': ( '%p %p', (0, 29), (1, 30)),
|
||||
'cmp_list1': ( '%[3]{pattr} %p %p', (0, 19), (-2, 19)),
|
||||
'cmp_list2': ( '%[1]{pattr} %p', (0, 19)),
|
||||
'ret_or': ( '%c or %c', 0, 2 ),
|
||||
'conditional': ( '%p if %p else %p', (2, 27), (0, 27), (4, 27) ),
|
||||
'conditionalTrue': ( '%p if 1 else %p', (0, 27), (2, 27) ),
|
||||
'ret_cond': ( '%p if %p else %p', (2, 27), (0, 27), (-1, 27) ),
|
||||
'conditionalnot': ( '%p if not %p else %p', (2, 27), (0, 22), (4, 27) ),
|
||||
'ret_cond_not': ( '%p if not %p else %p', (2, 27), (0, 22), (-1, 27) ),
|
||||
'conditional_lambda': ( '%c if %c else %c', 2, 0, 4),
|
||||
|
||||
'compare': ( '%p %[-1]{pattr.replace("-", " ")} %p', (0, 19), (1, 19) ),
|
||||
'cmp_list': ( '%p %p', (0, 29), (1, 30)),
|
||||
'cmp_list1': ( '%[3]{pattr} %p %p', (0, 19), (-2, 19)),
|
||||
'cmp_list2': ( '%[1]{pattr} %p', (0, 19)),
|
||||
# 'classdef': (), # handled by n_classdef()
|
||||
'funcdef': ( '\n\n%|def %c\n', -2), # -2 to handle closures
|
||||
'funcdef': ( '\n\n%|def %c\n', -2), # -2 to handle closures
|
||||
'funcdefdeco': ( '\n\n%c', 0),
|
||||
'mkfuncdeco': ( '%|@%c\n%c', 0, 1),
|
||||
'mkfuncdeco': ( '%|@%c\n%c', 0, 1),
|
||||
'mkfuncdeco0': ( '%|def %c\n', 0),
|
||||
'classdefdeco': ( '\n\n%c', 0),
|
||||
'classdefdeco1': ( '%|@%c\n%c', 0, 1),
|
||||
'kwarg': ( '%[0]{pattr}=%c', 1),
|
||||
'kwargs': ( '%D', (0, maxint, ', ') ),
|
||||
'kwarg': ( '%[0]{pattr}=%c', 1),
|
||||
'kwargs': ( '%D', (0, maxint, ', ') ),
|
||||
|
||||
'assert_expr_or': ( '%c or %c', 0, 2 ),
|
||||
'assert_expr_and': ( '%c and %c', 0, 2 ),
|
||||
'print_items_stmt': ( '%|print %c%c,\n', 0, 2), # Python 2 only
|
||||
'print_items_nl_stmt': ( '%|print %c%c\n', 0, 2),
|
||||
'print_item': ( ', %c', 0),
|
||||
'print_nl': ( '%|print\n', ),
|
||||
'print_to': ( '%|print >> %c, %c,\n', 0, 1 ),
|
||||
'print_to_nl': ( '%|print >> %c, %c\n', 0, 1 ),
|
||||
'print_nl_to': ( '%|print >> %c\n', 0 ),
|
||||
'assert_expr_or': ( '%c or %c', 0, 2 ),
|
||||
'assert_expr_and': ( '%c and %c', 0, 2 ),
|
||||
'print_items_stmt': ( '%|print %c%c,\n', 0, 2 ), # Python 2 only
|
||||
'print_items_nl_stmt': ( '%|print %c%c\n', 0, 2 ),
|
||||
'print_item': ( ', %c', 0),
|
||||
'print_nl': ( '%|print\n', ),
|
||||
'print_to': ( '%|print >> %c, %c,\n', 0, 1 ),
|
||||
'print_to_nl': ( '%|print >> %c, %c\n', 0, 1 ),
|
||||
'print_nl_to': ( '%|print >> %c\n', 0 ),
|
||||
'print_to_items': ( '%C', (0, 2, ', ') ),
|
||||
|
||||
'call_stmt': ( '%|%p\n', (0, 200)),
|
||||
'break_stmt': ( '%|break\n', ),
|
||||
'call_stmt': ( '%|%p\n', (0, 200)),
|
||||
'break_stmt': ( '%|break\n', ),
|
||||
'continue_stmt': ( '%|continue\n', ),
|
||||
|
||||
'raise_stmt0': ( '%|raise\n', ),
|
||||
'raise_stmt1': ( '%|raise %c\n', 0),
|
||||
'raise_stmt3': ( '%|raise %c, %c, %c\n', 0, 1, 2),
|
||||
'raise_stmt0': ( '%|raise\n', ),
|
||||
'raise_stmt1': ( '%|raise %c\n', 0),
|
||||
'raise_stmt3': ( '%|raise %c, %c, %c\n', 0, 1, 2),
|
||||
# 'yield': ( 'yield %c', 0),
|
||||
# 'return_stmt': ( '%|return %c\n', 0),
|
||||
'return_if_stmt': ( 'return %c\n', 0),
|
||||
|
||||
'ifstmt': ( '%|if %c:\n%+%c%-', 0, 1 ),
|
||||
'ifstmt': ( '%|if %c:\n%+%c%-', 0, 1 ),
|
||||
'iflaststmt': ( '%|if %c:\n%+%c%-', 0, 1 ),
|
||||
'iflaststmtl': ( '%|if %c:\n%+%c%-', 0, 1 ),
|
||||
'testtrue': ( 'not %p', (0, 22) ),
|
||||
@@ -229,37 +240,39 @@ TABLE_DIRECT = {
|
||||
'elifelsestmtr2': ( '%|elif %c:\n%+%c%-%|else:\n%+%c%-\n\n', 0, 1, 3 ), # has COME_FROM
|
||||
|
||||
'whileTruestmt': ( '%|while True:\n%+%c%-\n\n', 1 ),
|
||||
'whilestmt': ( '%|while %c:\n%+%c%-\n\n', 1, 2 ),
|
||||
'while1stmt': ( '%|while 1:\n%+%c%-\n\n', 1 ),
|
||||
'while1elsestmt': ( '%|while 1:\n%+%c%-%|else:\n%+%c%-\n\n', 1, -2 ),
|
||||
'whilestmt': ( '%|while %c:\n%+%c%-\n\n', 1, 2 ),
|
||||
'while1stmt': ( '%|while 1:\n%+%c%-\n\n', 1 ),
|
||||
'while1elsestmt': ( '%|while 1:\n%+%c%-%|else:\n%+%c%-\n\n', 1, -2 ),
|
||||
'whileelsestmt': ( '%|while %c:\n%+%c%-%|else:\n%+%c%-\n\n', 1, 2, -2 ),
|
||||
'whileelselaststmt': ( '%|while %c:\n%+%c%-%|else:\n%+%c%-', 1, 2, -2 ),
|
||||
'forstmt': ( '%|for %c in %c:\n%+%c%-\n\n', 3, 1, 4 ),
|
||||
'forelsestmt': (
|
||||
'%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n', 3, 1, 4, -2),
|
||||
'forstmt': ( '%|for %c in %c:\n%+%c%-\n\n', 3, 1, 4 ),
|
||||
'forelsestmt': (
|
||||
'%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n', 3, 1, 4, -2 ),
|
||||
'forelselaststmt': (
|
||||
'%|for %c in %c:\n%+%c%-%|else:\n%+%c%-', 3, 1, 4, -2),
|
||||
'%|for %c in %c:\n%+%c%-%|else:\n%+%c%-', 3, 1, 4, -2 ),
|
||||
'forelselaststmtl': (
|
||||
'%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n', 3, 1, 4, -2),
|
||||
'trystmt': ( '%|try:\n%+%c%-%c\n\n', 1, 3 ),
|
||||
'tryelsestmt': ( '%|try:\n%+%c%-%c%|else:\n%+%c%-\n\n', 1, 3, 4 ),
|
||||
'tryelsestmtc': ( '%|try:\n%+%c%-%c%|else:\n%+%c%-', 1, 3, 4 ),
|
||||
'tryelsestmtl': ( '%|try:\n%+%c%-%c%|else:\n%+%c%-', 1, 3, 4 ),
|
||||
'tf_trystmt': ( '%c%-%c%+', 1, 3 ),
|
||||
'%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n', 3, 1, 4, -2 ),
|
||||
'trystmt': ( '%|try:\n%+%c%-%c\n\n', 1, 3 ),
|
||||
'tryelsestmt': ( '%|try:\n%+%c%-%c%|else:\n%+%c%-\n\n', 1, 3, 4 ),
|
||||
'tryelsestmtc': ( '%|try:\n%+%c%-%c%|else:\n%+%c%-', 1, 3, 4 ),
|
||||
'tryelsestmtl': ( '%|try:\n%+%c%-%c%|else:\n%+%c%-', 1, 3, 4 ),
|
||||
'tf_trystmt': ( '%c%-%c%+', 1, 3 ),
|
||||
'tf_tryelsestmt': ( '%c%-%c%|else:\n%+%c', 1, 3, 4 ),
|
||||
'tryfinallystmt': ( '%|try:\n%+%c%-%|finally:\n%+%c%-\n\n', 1, 5 ),
|
||||
'except': ( '%|except:\n%+%c%-', 3 ),
|
||||
'except_cond1': ( '%|except %c:\n', 1 ),
|
||||
'except_cond1': ( '%|except %c:\n', 1 ),
|
||||
'except_suite': ( '%+%c%-%C', 0, (1, maxint, '') ),
|
||||
'except_suite_finalize': ( '%+%c%-%C', 1, (3, maxint, '') ),
|
||||
'passstmt': ( '%|pass\n', ),
|
||||
'STORE_FAST': ( '%{pattr}', ),
|
||||
'kv': ( '%c: %c', 3, 1 ),
|
||||
'kv2': ( '%c: %c', 1, 2 ),
|
||||
'mapexpr': ( '{%[1]C}', (0, maxint, ', ') ),
|
||||
'importstmt': ( '%|import %c\n', 2),
|
||||
'importfrom': ( '%|from %[2]{pattr} import %c\n', 3 ),
|
||||
'importstar': ( '%|from %[2]{pattr} import *\n', ),
|
||||
'passstmt': ( '%|pass\n', ),
|
||||
'STORE_FAST': ( '%{pattr}', ),
|
||||
'kv': ( '%c: %c', 3, 1 ),
|
||||
'kv2': ( '%c: %c', 1, 2 ),
|
||||
'mapexpr': ( '{%[1]C}', (0, maxint, ', ') ),
|
||||
'importstmt': ( '%|import %c\n', 2),
|
||||
'importlist': ( '%C', (0, maxint, ', ') ),
|
||||
'importfrom': ( '%|from %[2]{pattr} import %c\n',
|
||||
(3, 'importlist') ),
|
||||
'importstar': ( '%|from %[2]{pattr} import *\n', ),
|
||||
}
|
||||
|
||||
|
||||
@@ -276,7 +289,7 @@ MAP = {
|
||||
}
|
||||
|
||||
# Operator precidence
|
||||
# See https://docs.python.org/3/reference/expressions.html
|
||||
# See https://docs.python.org/2/reference/expressions.html
|
||||
# or https://docs.python.org/3/reference/expressions.html
|
||||
# for a list.
|
||||
PRECEDENCE = {
|
||||
@@ -332,6 +345,7 @@ PRECEDENCE = {
|
||||
'ret_or': 26,
|
||||
|
||||
'conditional': 28,
|
||||
'conditional_lamdba': 28,
|
||||
'conditionalnot': 28,
|
||||
'ret_cond': 28,
|
||||
'ret_cond_not': 28,
|
||||
|
@@ -1,6 +1,4 @@
|
||||
# Copyright (c) 2015, 2016 by Rocky Bernstein
|
||||
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
|
||||
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
||||
# Copyright (c) 2015-2017 by Rocky Bernstein
|
||||
# Copyright (c) 1999 John Aycock
|
||||
|
||||
"""
|
||||
@@ -8,8 +6,8 @@ Creates Python source code from an uncompyle6 abstract syntax tree,
|
||||
and indexes fragments which can be accessed by instruction offset
|
||||
address.
|
||||
|
||||
See the comments in pysource for information on the abstract sytax tree
|
||||
and how semantic actions are written.
|
||||
See https://github.com/rocky/python-uncompyle6/wiki/Table-driven-semantic-actions.
|
||||
for a more complete explanation, nicely marked up and with examples.
|
||||
|
||||
We add some format specifiers here not used in pysource
|
||||
|
||||
@@ -40,7 +38,8 @@ do it recursively which is where offsets are probably located.
|
||||
2. %b
|
||||
-----
|
||||
|
||||
%b associates the text from the previous start node up to what we have now
|
||||
%b associates the text from the specified index to what we have now.
|
||||
it takes an integer argument.
|
||||
|
||||
For example in:
|
||||
'importmultiple': ( '%|import%b %c%c\n', 0, 2, 3 ),
|
||||
@@ -96,9 +95,8 @@ TABLE_DIRECT_FRAGMENT = {
|
||||
'importfrom': ( '%|from %[2]{pattr}%x import %c\n', (2, (0, 1)), 3),
|
||||
'importmultiple': ( '%|import%b %c%c\n', 0, 2, 3 ),
|
||||
'list_for': (' for %c%x in %c%c', 2, (2, (1, )), 0, 3 ),
|
||||
'forstmt': ( '%|for%b %c%x in %c:\n%+%c%-\n\n', 0, 3, (3, (2, )), 1, 4 ),
|
||||
'forelsestmt': (
|
||||
'%|for %c in %c%x:\n%+%c%-%|else:\n%+%c%-\n\n', 3, (3, (2,)), 1, 4, -2),
|
||||
'%|for %c%x in %c:\n%+%c%-%|else:\n%+%c%-\n\n', 3, (3, (2,)), 1, 4, -2),
|
||||
'forelselaststmt': (
|
||||
'%|for %c%x in %c:\n%+%c%-%|else:\n%+%c%-', 3, (3, (2,)), 1, 4, -2),
|
||||
'forelselaststmtl': (
|
||||
@@ -119,11 +117,12 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
|
||||
def __init__(self, version, scanner, showast=False,
|
||||
debug_parser=PARSER_DEFAULT_DEBUG,
|
||||
compile_mode='exec', is_pypy=False):
|
||||
compile_mode='exec', is_pypy=False, tolerate_errors=True):
|
||||
pysource.SourceWalker.__init__(self, version=version, out=StringIO(),
|
||||
scanner=scanner,
|
||||
showast=showast, debug_parser=debug_parser,
|
||||
compile_mode=compile_mode, is_pypy=is_pypy)
|
||||
compile_mode=compile_mode, is_pypy=is_pypy,
|
||||
tolerate_errors=tolerate_errors)
|
||||
|
||||
# hide_internal suppresses displaying the additional instructions that sometimes
|
||||
# exist in code but but were not written in the source code.
|
||||
@@ -139,6 +138,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
# FIXME: is there a better way?
|
||||
global MAP_DIRECT_FRAGMENT
|
||||
MAP_DIRECT_FRAGMENT = dict(TABLE_DIRECT, **TABLE_DIRECT_FRAGMENT),
|
||||
return
|
||||
|
||||
f = property(lambda s: s.params['f'],
|
||||
lambda s, x: s.params.__setitem__('f', x),
|
||||
@@ -310,11 +310,11 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
def n_expr(self, node):
|
||||
start = len(self.f.getvalue())
|
||||
p = self.prec
|
||||
if node[0].type.startswith('binary_expr'):
|
||||
if node[0].kind.startswith('binary_expr'):
|
||||
n = node[0][-1][0]
|
||||
else:
|
||||
n = node[0]
|
||||
self.prec = PRECEDENCE.get(n.type, -2)
|
||||
self.prec = PRECEDENCE.get(n.kind, -2)
|
||||
if n == 'LOAD_CONST' and repr(n.pattr)[0] == '-':
|
||||
n.parent = node
|
||||
self.set_pos_info(n, start, len(self.f.getvalue()))
|
||||
@@ -407,7 +407,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
def n_ifelsestmtr(self, node):
|
||||
if node[2] == 'COME_FROM':
|
||||
return_stmts_node = node[3]
|
||||
node.type = 'ifelsestmtr2'
|
||||
node.kind = 'ifelsestmtr2'
|
||||
else:
|
||||
return_stmts_node = node[2]
|
||||
if len(return_stmts_node) != 2:
|
||||
@@ -424,10 +424,10 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
self.write(self.indent, 'if ')
|
||||
self.preorder(node[0])
|
||||
self.println(':')
|
||||
self.indentMore()
|
||||
self.indent_more()
|
||||
node[1].parent = node
|
||||
self.preorder(node[1])
|
||||
self.indentLess()
|
||||
self.indent_less()
|
||||
|
||||
if_ret_at_end = False
|
||||
if len(node[2][0]) >= 3:
|
||||
@@ -440,23 +440,23 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
for n in return_stmts_node[0]:
|
||||
if (n[0] == 'ifstmt' and n[0][1][0] == 'return_if_stmts'):
|
||||
if prev_stmt_is_if_ret:
|
||||
n[0].type = 'elifstmt'
|
||||
n[0].kind = 'elifstmt'
|
||||
prev_stmt_is_if_ret = True
|
||||
else:
|
||||
prev_stmt_is_if_ret = False
|
||||
if not past_else and not if_ret_at_end:
|
||||
self.println(self.indent, 'else:')
|
||||
self.indentMore()
|
||||
self.indent_more()
|
||||
past_else = True
|
||||
n.parent = node
|
||||
self.preorder(n)
|
||||
if not past_else or if_ret_at_end:
|
||||
self.println(self.indent, 'else:')
|
||||
self.indentMore()
|
||||
self.indent_more()
|
||||
node[2][1].parent = node
|
||||
self.preorder(node[2][1])
|
||||
self.set_pos_info(node, start, len(self.f.getvalue()))
|
||||
self.indentLess()
|
||||
self.indent_less()
|
||||
self.prune()
|
||||
|
||||
def n_elifelsestmtr(self, node):
|
||||
@@ -473,20 +473,20 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
node[0].parent = node
|
||||
self.preorder(node[0])
|
||||
self.println(':')
|
||||
self.indentMore()
|
||||
self.indent_more()
|
||||
node[1].parent = node
|
||||
self.preorder(node[1])
|
||||
self.indentLess()
|
||||
self.indent_less()
|
||||
|
||||
for n in node[2][0]:
|
||||
n[0].type = 'elifstmt'
|
||||
n[0].kind = 'elifstmt'
|
||||
n.parent = node
|
||||
self.preorder(n)
|
||||
self.println(self.indent, 'else:')
|
||||
self.indentMore()
|
||||
self.indent_more()
|
||||
node[2][1].parent = node
|
||||
self.preorder(node[2][1])
|
||||
self.indentLess()
|
||||
self.indent_less()
|
||||
self.set_pos_info(node, start, len(self.f.getvalue()))
|
||||
self.prune()
|
||||
|
||||
@@ -495,7 +495,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
iname = node[0].pattr
|
||||
|
||||
store_import_node = node[-1][-1]
|
||||
assert store_import_node.type.startswith('STORE_')
|
||||
assert store_import_node.kind.startswith('STORE_')
|
||||
|
||||
sname = store_import_node.pattr
|
||||
self.write(iname)
|
||||
@@ -530,7 +530,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
self.write(func_name)
|
||||
self.set_pos_info(code_node, start, len(self.f.getvalue()))
|
||||
|
||||
self.indentMore()
|
||||
self.indent_more()
|
||||
start = len(self.f.getvalue())
|
||||
self.make_function(node, isLambda=False, codeNode=code_node)
|
||||
|
||||
@@ -540,7 +540,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
self.write('\n\n')
|
||||
else:
|
||||
self.write('\n\n\n')
|
||||
self.indentLess()
|
||||
self.indent_less()
|
||||
self.prune() # stop recursing
|
||||
|
||||
def n_list_compr(self, node):
|
||||
@@ -556,7 +556,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
elif n == 'list_if': n = n[2]
|
||||
elif n == 'list_if_not': n= n[2]
|
||||
assert n == 'lc_body'
|
||||
if node[0].type.startswith('BUILD_LIST'):
|
||||
if node[0].kind.startswith('BUILD_LIST'):
|
||||
start = len(self.f.getvalue())
|
||||
self.set_pos_info(node[0], start, start+1)
|
||||
self.write( '[ ')
|
||||
@@ -689,7 +689,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
|
||||
# Python 2.7+ starts including set_comp_body
|
||||
# Python 3.5+ starts including setcomp_func
|
||||
assert n.type in ('lc_body', 'comp_body', 'setcomp_func', 'set_comp_body'), ast
|
||||
assert n.kind in ('lc_body', 'comp_body', 'setcomp_func', 'set_comp_body'), ast
|
||||
assert designator, "Couldn't find designator in list/set comprehension"
|
||||
|
||||
old_name = self.name
|
||||
@@ -716,7 +716,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
self.preorder(if_node)
|
||||
self.prec = p
|
||||
self.name = old_name
|
||||
if node[-1].type.startswith('CALL_FUNCTION'):
|
||||
if node[-1].kind.startswith('CALL_FUNCTION'):
|
||||
self.set_pos_info(node[-1], gen_start, len(self.f.getvalue()))
|
||||
|
||||
def listcomprehension_walk2(self, node):
|
||||
@@ -745,7 +745,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
n = n[3]
|
||||
elif n in ('list_if', 'list_if_not'):
|
||||
# FIXME: just a guess
|
||||
if n[0].type == 'expr':
|
||||
if n[0].kind == 'expr':
|
||||
list_if = n
|
||||
else:
|
||||
list_if = n[1]
|
||||
@@ -791,7 +791,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
start = len(self.f.getvalue())
|
||||
self.set_pos_info(node[0], start-1, start)
|
||||
self.comprehension_walk3(node, 1, 0)
|
||||
elif node[0].type == 'load_closure':
|
||||
elif node[0].kind == 'load_closure':
|
||||
self.setcomprehension_walk3(node, collection_index=4)
|
||||
else:
|
||||
self.comprehension_walk(node, iter_index=4)
|
||||
@@ -810,7 +810,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
self.set_pos_info(node[0], start, len(self.f.getvalue()))
|
||||
self.write(': {')
|
||||
start = len(self.f.getvalue())
|
||||
assert node[0].type.startswith('BUILD_SET')
|
||||
assert node[0].kind.startswith('BUILD_SET')
|
||||
self.set_pos_info(node[0], start-1, start)
|
||||
designator = node[3]
|
||||
assert designator == 'designator'
|
||||
@@ -819,7 +819,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
fin = len(self.f.getvalue())
|
||||
self.set_pos_info(designator, start, fin)
|
||||
for_iter_node = node[2]
|
||||
assert for_iter_node.type == 'FOR_ITER'
|
||||
assert for_iter_node.kind == 'FOR_ITER'
|
||||
self.set_pos_info(for_iter_node, start, fin)
|
||||
self.write(" for ")
|
||||
self.preorder(designator)
|
||||
@@ -838,7 +838,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
|
||||
def n_listcomp(self, node):
|
||||
self.write('[')
|
||||
if node[0].type == 'load_closure':
|
||||
if node[0].kind == 'load_closure':
|
||||
self.listcomprehension_walk2(node)
|
||||
else:
|
||||
if node[0] == 'LOAD_LISTCOMP':
|
||||
@@ -852,7 +852,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
if len(node) > 1:
|
||||
if (node[0] == 'c_stmts_opt' and
|
||||
node[0][0] == 'passstmt' and
|
||||
node[1].type.startswith('JUMP_FORWARD')):
|
||||
node[1].kind.startswith('JUMP_FORWARD')):
|
||||
self.set_pos_info(node[1], node[0][0].start, node[0][0].finish)
|
||||
|
||||
def setcomprehension_walk3(self, node, collection_index):
|
||||
@@ -883,7 +883,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
n = n[3]
|
||||
elif n in ('list_if', 'list_if_not', 'comp_if', 'comp_if_not'):
|
||||
# FIXME: just a guess
|
||||
if n[0].type == 'expr':
|
||||
if n[0].kind == 'expr':
|
||||
list_if = n
|
||||
else:
|
||||
list_if = n[1]
|
||||
@@ -989,9 +989,9 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
self.println(':')
|
||||
|
||||
# class body
|
||||
self.indentMore()
|
||||
self.indent_more()
|
||||
self.build_class(subclass)
|
||||
self.indentLess()
|
||||
self.indent_less()
|
||||
|
||||
self.currentclass = cclass
|
||||
self.set_pos_info(node, start, len(self.f.getvalue()))
|
||||
@@ -1046,8 +1046,8 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
# NOTE: this differs from behavior in pysource.py
|
||||
|
||||
if len(tokens) >= 2 and not noneInNames:
|
||||
if tokens[-1].type == 'RETURN_VALUE':
|
||||
if tokens[-2].type != 'LOAD_CONST':
|
||||
if tokens[-1].kind == 'RETURN_VALUE':
|
||||
if tokens[-2].kind != 'LOAD_CONST':
|
||||
tokens.append(Token('RETURN_LAST'))
|
||||
if len(tokens) == 0:
|
||||
return
|
||||
@@ -1301,10 +1301,10 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
# as a custom rule
|
||||
start = len(self.f.getvalue())
|
||||
n = len(node)-1
|
||||
assert node[n].type.startswith('CALL_FUNCTION')
|
||||
assert node[n].kind.startswith('CALL_FUNCTION')
|
||||
|
||||
for i in range(n-2, 0, -1):
|
||||
if not node[i].type in ['expr', 'LOAD_CLASSNAME']:
|
||||
if not node[i].kind in ['expr', 'LOAD_CLASSNAME']:
|
||||
break
|
||||
pass
|
||||
|
||||
@@ -1332,14 +1332,14 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
p = self.prec
|
||||
self.prec = 100
|
||||
|
||||
self.indentMore(INDENT_PER_LEVEL)
|
||||
self.indent_more(INDENT_PER_LEVEL)
|
||||
line_seperator = ',\n' + self.indent
|
||||
sep = INDENT_PER_LEVEL[:-1]
|
||||
start = len(self.f.getvalue())
|
||||
self.write('{')
|
||||
|
||||
if self.version > 3.0:
|
||||
if node[0].type.startswith('kvlist'):
|
||||
if node[0].kind.startswith('kvlist'):
|
||||
# Python 3.5+ style key/value list in mapexpr
|
||||
kv_node = node[0]
|
||||
l = list(kv_node)
|
||||
@@ -1354,11 +1354,11 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
i += 2
|
||||
pass
|
||||
pass
|
||||
elif node[1].type.startswith('kvlist'):
|
||||
elif node[1].kind.startswith('kvlist'):
|
||||
# Python 3.0..3.4 style key/value list in mapexpr
|
||||
kv_node = node[1]
|
||||
l = list(kv_node)
|
||||
if len(l) > 0 and l[0].type == 'kv3':
|
||||
if len(l) > 0 and l[0].kind == 'kv3':
|
||||
# Python 3.2 does this
|
||||
kv_node = node[1][0]
|
||||
l = list(kv_node)
|
||||
@@ -1381,7 +1381,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
pass
|
||||
else:
|
||||
# Python 2 style kvlist
|
||||
assert node[-1].type.startswith('kvlist')
|
||||
assert node[-1].kind.startswith('kvlist')
|
||||
kv_node = node[-1] # goto kvlist
|
||||
|
||||
for kv in kv_node:
|
||||
@@ -1409,7 +1409,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
n.parent = node
|
||||
self.set_pos_info(n, start, finish)
|
||||
self.set_pos_info(node, start, finish)
|
||||
self.indentLess(INDENT_PER_LEVEL)
|
||||
self.indent_less(INDENT_PER_LEVEL)
|
||||
self.prec = p
|
||||
self.prune()
|
||||
|
||||
@@ -1420,7 +1420,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
p = self.prec
|
||||
self.prec = 100
|
||||
n = node.pop()
|
||||
lastnode = n.type
|
||||
lastnode = n.kind
|
||||
start = len(self.f.getvalue())
|
||||
if lastnode.startswith('BUILD_LIST'):
|
||||
self.write('['); endchar = ']'
|
||||
@@ -1445,7 +1445,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
else:
|
||||
flat_elems.append(elem)
|
||||
|
||||
self.indentMore(INDENT_PER_LEVEL)
|
||||
self.indent_more(INDENT_PER_LEVEL)
|
||||
if len(node) > 3:
|
||||
line_separator = ',\n' + self.indent
|
||||
else:
|
||||
@@ -1470,14 +1470,14 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
n.parent = node.parent
|
||||
self.set_pos_info(n, start, finish)
|
||||
self.set_pos_info(node, start, finish)
|
||||
self.indentLess(INDENT_PER_LEVEL)
|
||||
self.indent_less(INDENT_PER_LEVEL)
|
||||
self.prec = p
|
||||
self.prune()
|
||||
|
||||
def engine(self, entry, startnode):
|
||||
def template_engine(self, entry, startnode):
|
||||
"""The format template interpetation engine. See the comment at the
|
||||
beginning of this module for the how we interpret format specifications such as
|
||||
%c, %C, and so on.
|
||||
beginning of this module for the how we interpret format
|
||||
specifications such as %c, %C, and so on.
|
||||
"""
|
||||
|
||||
# print("-----")
|
||||
@@ -1514,8 +1514,8 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
self.write('%')
|
||||
self.set_pos_info(node, start, len(self.f.getvalue()))
|
||||
|
||||
elif typ == '+': self.indentMore()
|
||||
elif typ == '-': self.indentLess()
|
||||
elif typ == '+': self.indent_more()
|
||||
elif typ == '-': self.indent_less()
|
||||
elif typ == '|': self.write(self.indent)
|
||||
# no longer used, since BUILD_TUPLE_n is pretty printed:
|
||||
elif typ == 'r': recurse_node = True
|
||||
@@ -1528,14 +1528,24 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
arg += 1
|
||||
elif typ == 'c':
|
||||
start = len(self.f.getvalue())
|
||||
self.preorder(node[entry[arg]])
|
||||
|
||||
index = entry[arg]
|
||||
if isinstance(index, tuple):
|
||||
assert node[index[0]] == index[1], (
|
||||
"at %s[%d], %s vs %s" % (
|
||||
node.kind, arg, node[index[0]].kind, index[1])
|
||||
)
|
||||
index = index[0]
|
||||
if isinstance(index, int):
|
||||
self.preorder(node[index])
|
||||
|
||||
finish = len(self.f.getvalue())
|
||||
|
||||
# FIXME rocky: figure out how to get this to be table driven
|
||||
# for loops have two positions that correspond to a single text
|
||||
# location. In "for i in ..." there is the initialization "i" code as well
|
||||
# as the iteration code with "i"
|
||||
match = re.search(r'^for', startnode.type)
|
||||
match = re.search(r'^for', startnode.kind)
|
||||
if match and entry[arg] == 3:
|
||||
self.set_pos_info(node[0], start, finish)
|
||||
for n in node[2]:
|
||||
@@ -1629,7 +1639,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
# 2. subroutine calls. It the last op is the call and for purposes of printing
|
||||
# we don't need to print anything special there. However it encompases the
|
||||
# entire string of the node fn(...)
|
||||
match = re.search(r'^call_function', startnode.type)
|
||||
match = re.search(r'^call_function', startnode.kind)
|
||||
if match:
|
||||
last_node = startnode[-1]
|
||||
# import traceback; traceback.print_stack()
|
||||
@@ -1770,7 +1780,7 @@ if __name__ == '__main__':
|
||||
nodeInfo = walk.offsets[name, offset]
|
||||
node = nodeInfo.node
|
||||
extractInfo = walk.extract_node_info(node)
|
||||
print("code: %s" % node.type)
|
||||
print("code: %s" % node.kind)
|
||||
# print extractInfo
|
||||
print(extractInfo.selectedText)
|
||||
print(extractInfo.selectedLine)
|
||||
@@ -1780,7 +1790,7 @@ if __name__ == '__main__':
|
||||
print("Contained in...")
|
||||
print(extractInfo.selectedLine)
|
||||
print(extractInfo.markerLine)
|
||||
print("code: %s" % p.type)
|
||||
print("code: %s" % p.kind)
|
||||
print('=' * 40)
|
||||
pass
|
||||
pass
|
||||
@@ -1799,7 +1809,7 @@ if __name__ == '__main__':
|
||||
nodeInfo = walk.offsets[name, offset]
|
||||
node = nodeInfo.node
|
||||
extractInfo = walk.extract_node_info(node)
|
||||
print("code: %s" % node.type)
|
||||
print("code: %s" % node.kind)
|
||||
# print extractInfo
|
||||
print(extractInfo.selectedText)
|
||||
print(extractInfo.selectedLine)
|
||||
@@ -1809,7 +1819,7 @@ if __name__ == '__main__':
|
||||
print("Contained in...")
|
||||
print(extractInfo.selectedLine)
|
||||
print(extractInfo.markerLine)
|
||||
print("code: %s" % p.type)
|
||||
print("code: %s" % p.kind)
|
||||
print('=' * 40)
|
||||
pass
|
||||
pass
|
||||
|
@@ -17,7 +17,7 @@ def find_all_globals(node, globs):
|
||||
for n in node:
|
||||
if isinstance(n, AST):
|
||||
globs = find_all_globals(n, globs)
|
||||
elif n.type in ('STORE_GLOBAL', 'DELETE_GLOBAL', 'LOAD_GLOBAL'):
|
||||
elif n.kind in ('STORE_GLOBAL', 'DELETE_GLOBAL', 'LOAD_GLOBAL'):
|
||||
globs.add(n.pattr)
|
||||
return globs
|
||||
|
||||
@@ -26,7 +26,7 @@ def find_globals(node, globs):
|
||||
for n in node:
|
||||
if isinstance(n, AST):
|
||||
globs = find_globals(n, globs)
|
||||
elif n.type in ('STORE_GLOBAL', 'DELETE_GLOBAL'):
|
||||
elif n.kind in ('STORE_GLOBAL', 'DELETE_GLOBAL'):
|
||||
globs.add(n.pattr)
|
||||
return globs
|
||||
|
||||
@@ -36,7 +36,7 @@ def find_none(node):
|
||||
if n not in ('return_stmt', 'return_if_stmt'):
|
||||
if find_none(n):
|
||||
return True
|
||||
elif n.type == 'LOAD_CONST' and n.pattr is None:
|
||||
elif n.kind == 'LOAD_CONST' and n.pattr is None:
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -64,7 +64,7 @@ def make_function3_annotate(self, node, isLambda, nested=1,
|
||||
return name
|
||||
|
||||
# MAKE_FUNCTION_... or MAKE_CLOSURE_...
|
||||
assert node[-1].type.startswith('MAKE_')
|
||||
assert node[-1].kind.startswith('MAKE_')
|
||||
|
||||
annotate_tuple = None
|
||||
for annotate_last in range(len(node)-1, -1, -1):
|
||||
@@ -80,7 +80,7 @@ def make_function3_annotate(self, node, isLambda, nested=1,
|
||||
i = -1
|
||||
j = annotate_last-1
|
||||
l = -len(node)
|
||||
while j >= l and node[j].type in ('annotate_arg' 'annotate_tuple'):
|
||||
while j >= l and node[j].kind in ('annotate_arg' 'annotate_tuple'):
|
||||
annotate_args[annotate_tup[i]] = node[j][0]
|
||||
i -= 1
|
||||
j -= 1
|
||||
@@ -106,7 +106,7 @@ def make_function3_annotate(self, node, isLambda, nested=1,
|
||||
lambda_index = None
|
||||
|
||||
if lambda_index and isLambda and iscode(node[lambda_index].attr):
|
||||
assert node[lambda_index].type == 'LOAD_LAMBDA'
|
||||
assert node[lambda_index].kind == 'LOAD_LAMBDA'
|
||||
code = node[lambda_index].attr
|
||||
else:
|
||||
code = codeNode.attr
|
||||
@@ -125,7 +125,8 @@ def make_function3_annotate(self, node, isLambda, nested=1,
|
||||
noneInNames = ('None' in code.co_names))
|
||||
except ParserError, p:
|
||||
self.write(str(p))
|
||||
self.ERROR = p
|
||||
if not self.tolerate_errors:
|
||||
self.ERROR = p
|
||||
return
|
||||
|
||||
kw_pairs = args_node.attr[1]
|
||||
@@ -318,7 +319,7 @@ def make_function2(self, node, isLambda, nested=1, codeNode=None):
|
||||
return name
|
||||
|
||||
# MAKE_FUNCTION_... or MAKE_CLOSURE_...
|
||||
assert node[-1].type.startswith('MAKE_')
|
||||
assert node[-1].kind.startswith('MAKE_')
|
||||
|
||||
args_node = node[-1]
|
||||
if isinstance(args_node.attr, tuple):
|
||||
@@ -334,7 +335,7 @@ def make_function2(self, node, isLambda, nested=1, codeNode=None):
|
||||
lambda_index = None
|
||||
|
||||
if lambda_index and isLambda and iscode(node[lambda_index].attr):
|
||||
assert node[lambda_index].type == 'LOAD_LAMBDA'
|
||||
assert node[lambda_index].kind == 'LOAD_LAMBDA'
|
||||
code = node[lambda_index].attr
|
||||
else:
|
||||
code = codeNode.attr
|
||||
@@ -356,7 +357,8 @@ def make_function2(self, node, isLambda, nested=1, codeNode=None):
|
||||
noneInNames = ('None' in code.co_names))
|
||||
except ParserError, p:
|
||||
self.write(str(p))
|
||||
self.ERROR = p
|
||||
if not self.tolerate_errors:
|
||||
self.ERROR = p
|
||||
return
|
||||
|
||||
if self.version >= 3.0:
|
||||
@@ -450,7 +452,7 @@ def make_function3(self, node, isLambda, nested=1, codeNode=None):
|
||||
return name
|
||||
|
||||
# MAKE_FUNCTION_... or MAKE_CLOSURE_...
|
||||
assert node[-1].type.startswith('MAKE_')
|
||||
assert node[-1].kind.startswith('MAKE_')
|
||||
|
||||
args_node = node[-1]
|
||||
if isinstance(args_node.attr, tuple):
|
||||
@@ -484,7 +486,7 @@ def make_function3(self, node, isLambda, nested=1, codeNode=None):
|
||||
lambda_index = None
|
||||
|
||||
if lambda_index and isLambda and iscode(node[lambda_index].attr):
|
||||
assert node[lambda_index].type == 'LOAD_LAMBDA'
|
||||
assert node[lambda_index].kind == 'LOAD_LAMBDA'
|
||||
code = node[lambda_index].attr
|
||||
else:
|
||||
code = codeNode.attr
|
||||
@@ -507,7 +509,8 @@ def make_function3(self, node, isLambda, nested=1, codeNode=None):
|
||||
noneInNames = ('None' in code.co_names))
|
||||
except ParserError, p:
|
||||
self.write(str(p))
|
||||
self.ERROR = p
|
||||
if not self.tolerate_errors:
|
||||
self.ERROR = p
|
||||
return
|
||||
|
||||
if self.version >= 3.0:
|
||||
@@ -585,7 +588,7 @@ def make_function3(self, node, isLambda, nested=1, codeNode=None):
|
||||
for n in node:
|
||||
if n == 'pos_arg':
|
||||
continue
|
||||
elif self.version >= 3.4 and not (n.type in ('kwargs', 'kwarg')):
|
||||
elif self.version >= 3.4 and not (n.kind in ('kwargs', 'kwarg')):
|
||||
continue
|
||||
else:
|
||||
self.preorder(n)
|
||||
|
@@ -11,62 +11,105 @@ and what they mean).
|
||||
|
||||
Upper levels of the grammar is a more-or-less conventional grammar for
|
||||
Python.
|
||||
|
||||
Semantic action rules for nonterminal symbols can be specified here by
|
||||
creating a method prefaced with "n_" for that nonterminal. For
|
||||
example, "n_exec_stmt" handles the semantic actions for the
|
||||
"exec_smnt" nonterminal symbol. Similarly if a method with the name
|
||||
of the nonterminal is suffixed with "_exit" it will be called after
|
||||
all of its children are called.
|
||||
|
||||
Another other way to specify a semantic rule for a nonterminal is via
|
||||
rule given in one of the tables MAP_R0, MAP_R, or MAP_DIRECT.
|
||||
|
||||
These uses a printf-like syntax to direct substitution from attributes
|
||||
of the nonterminal and its children..
|
||||
|
||||
The rest of the below describes how table-driven semantic actions work
|
||||
and gives a list of the format specifiers. The default() and engine()
|
||||
methods implement most of the below.
|
||||
|
||||
Step 1 determines a table (T) and a path to a
|
||||
table key (K) from the node type (N) (other nodes are shown as O):
|
||||
|
||||
N N N&K
|
||||
/ | ... \ / | ... \ / | ... \
|
||||
O O O O O K O O O
|
||||
|
|
||||
K
|
||||
|
||||
MAP_R0 (TABLE_R0) MAP_R (TABLE_R) MAP_DIRECT (TABLE_DIRECT)
|
||||
|
||||
The default is a direct mapping. The key K is then extracted from the
|
||||
subtree and used to find a table entry T[K], if any. The result is a
|
||||
format string and arguments (a la printf()) for the formatting engine.
|
||||
Escapes in the format string are:
|
||||
|
||||
%c evaluate children N[A] recursively*
|
||||
%C evaluate children N[A[0]]..N[A[1]-1] recursively, separate by A[2]*
|
||||
%P same as %C but sets operator precedence
|
||||
%D same as %C but is for left-recursive lists like kwargs which
|
||||
goes to epsilon at the beginning. Using %C an extra separator
|
||||
with an epsilon appears at the beginning
|
||||
%, print ',' if last %C only printed one item. This is mostly for tuples
|
||||
on the LHS of an assignment statement since BUILD_TUPLE_n pretty-prints
|
||||
other tuples.
|
||||
%| tab to current indentation level
|
||||
%+ increase current indentation level
|
||||
%- decrease current indentation level
|
||||
%{...} evaluate ... in context of N
|
||||
%% literal '%'
|
||||
%p evaluate N setting precedence
|
||||
|
||||
|
||||
* indicates an argument (A) required.
|
||||
|
||||
The '%' may optionally be followed by a number (C) in square brackets, which
|
||||
makes the engine walk down to N[C] before evaluating the escape code.
|
||||
"""
|
||||
|
||||
# The below is a bit long, but still it is somehwat abbreviated.
|
||||
# See https://github.com/rocky/python-uncompyle6/wiki/Table-driven-semantic-actions.
|
||||
# for a more complete explanation, nicely marked up and with examples.
|
||||
#
|
||||
#
|
||||
# Semantic action rules for nonterminal symbols can be specified here by
|
||||
# creating a method prefaced with "n_" for that nonterminal. For
|
||||
# example, "n_exec_stmt" handles the semantic actions for the
|
||||
# "exec_stmt" nonterminal symbol. Similarly if a method with the name
|
||||
# of the nonterminal is suffixed with "_exit" it will be called after
|
||||
# all of its children are called.
|
||||
#
|
||||
# After a while writing methods this way, you'll find many routines which do similar
|
||||
# sorts of things, and soon you'll find you want a short notation to
|
||||
# describe rules and not have to create methods at all.
|
||||
#
|
||||
# So another other way to specify a semantic rule for a nonterminal is via
|
||||
# one of the tables MAP_R0, MAP_R, or MAP_DIRECT where the key is the
|
||||
# nonterminal name.
|
||||
#
|
||||
# These dictionaries use a printf-like syntax to direct substitution
|
||||
# from attributes of the nonterminal and its children..
|
||||
#
|
||||
# The rest of the below describes how table-driven semantic actions work
|
||||
# and gives a list of the format specifiers. The default() and
|
||||
# template_engine() methods implement most of the below.
|
||||
#
|
||||
# We allow for a couple of ways to interact with a node in a tree. So
|
||||
# step 1 after not seeing a custom method for a nonterminal is to
|
||||
# determine from what point of view tree-wise the rule is applied.
|
||||
|
||||
# In the diagram below, N is a nonterminal name, and K also a nonterminal
|
||||
# name but the one used as a key in the table.
|
||||
# we show where those are with respect to each other in the
|
||||
# AST tree for N.
|
||||
#
|
||||
#
|
||||
# N&K N N
|
||||
# / | ... \ / | ... \ / | ... \
|
||||
# O O O O O K O O O
|
||||
# |
|
||||
# K
|
||||
# TABLE_DIRECT TABLE_R TABLE_R0
|
||||
#
|
||||
# The default table is TABLE_DIRECT mapping By far, most rules used work this way.
|
||||
# TABLE_R0 is rarely used.
|
||||
#
|
||||
# The key K is then extracted from the subtree and used to find one
|
||||
# of the tables, T listed above. The result after applying T[K] is
|
||||
# a format string and arguments (a la printf()) for the formatting
|
||||
# engine.
|
||||
#
|
||||
# Escapes in the format string are:
|
||||
#
|
||||
# %c evaluate the node recursively. Its argument is a single
|
||||
# integer or tuple representing a node index.
|
||||
# If a tuple is given, the first item is the node index while
|
||||
# the second item is a string giving the node/noterminal name.
|
||||
# This name will be checked at runtime against the node type.
|
||||
#
|
||||
# %p like %c but sets the operator precedence.
|
||||
# Its argument then is a tuple indicating the node
|
||||
# index and the precidence value, an integer.
|
||||
#
|
||||
# %C evaluate children recursively, with sibling children separated by the
|
||||
# given string. It needs a 3-tuple: a starting node, the maximimum
|
||||
# value of an end node, and a string to be inserted between sibling children
|
||||
#
|
||||
# %, Append ',' if last %C only printed one item. This is mostly for tuples
|
||||
# on the LHS of an assignment statement since BUILD_TUPLE_n pretty-prints
|
||||
# other tuples. The specifier takes no arguments
|
||||
#
|
||||
# %P same as %C but sets operator precedence. Its argument is a 4-tuple:
|
||||
# the node low and high indices, the separator, a string the precidence
|
||||
# value, an integer.
|
||||
#
|
||||
# %D Same as `%C` this is for left-recursive lists like kwargs where goes
|
||||
# to epsilon at the beginning. It needs a 3-tuple: a starting node, the
|
||||
# maximimum value of an end node, and a string to be inserted between
|
||||
# sibling children. If we were to use `%C` an extra separator with an
|
||||
# epsilon would appear at the beginning.
|
||||
#
|
||||
# %| Insert spaces to the current indentation level. Takes no arguments.
|
||||
#
|
||||
# %+ increase current indentation level. Takes no arguments.
|
||||
#
|
||||
# %- decrease current indentation level. Takes no arguments.
|
||||
#
|
||||
# %{...} evaluate ... in context of N
|
||||
#
|
||||
# %% literal '%'. Takes no arguments.
|
||||
#
|
||||
#
|
||||
# The '%' may optionally be followed by a number (C) in square
|
||||
# brackets, which makes the template_engine walk down to N[C] before
|
||||
# evaluating the escape code.
|
||||
|
||||
import sys
|
||||
|
||||
from uncompyle6 import PYTHON3
|
||||
@@ -89,7 +132,7 @@ from uncompyle6.semantics.consts import (
|
||||
LINE_LENGTH, RETURN_LOCALS, NONE, RETURN_NONE, PASS,
|
||||
ASSIGN_DOC_STRING, NAME_MODULE, TAB,
|
||||
INDENT_PER_LEVEL, TABLE_R, TABLE_DIRECT, MAP_DIRECT,
|
||||
MAP, PRECEDENCE, ASSIGN_TUPLE_PARAM, escape, maxint, minint)
|
||||
MAP, PRECEDENCE, ASSIGN_TUPLE_PARAM, escape, minint)
|
||||
|
||||
|
||||
from uncompyle6.show import (
|
||||
@@ -103,7 +146,7 @@ else:
|
||||
|
||||
def is_docstring(node):
|
||||
try:
|
||||
return (node[0][0].type == 'assign' and
|
||||
return (node[0][0].kind == 'assign' and
|
||||
node[0][0][1][0].pattr == '__doc__')
|
||||
except:
|
||||
return False
|
||||
@@ -121,7 +164,30 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
def __init__(self, version, out, scanner, showast=False,
|
||||
debug_parser=PARSER_DEFAULT_DEBUG,
|
||||
compile_mode='exec', is_pypy=False,
|
||||
linestarts={}):
|
||||
linestarts={}, tolerate_errors=False):
|
||||
"""version is the Python version (a float) of the Python dialect
|
||||
|
||||
of both the AST and language we should produce.
|
||||
|
||||
out is IO-like file pointer to where the output should go. It
|
||||
whould have a getvalue() method.
|
||||
|
||||
scanner is a method to call when we need to scan tokens. Sometimes
|
||||
in producing output we will run across further tokens that need
|
||||
to be scaned.
|
||||
|
||||
If showast is True, we print the AST tree.
|
||||
|
||||
compile_mode is is either 'exec' or 'single'. It isthe compile
|
||||
mode that was used to create the AST and specifies a gramar variant within
|
||||
a Python version to use.
|
||||
|
||||
is_pypy should be True if the AST was generated for PyPy.
|
||||
|
||||
linestarts is a dictionary of line number to bytecode offset. This
|
||||
can sometimes assist in determinte which kind of source-code construct
|
||||
to use when there is ambiguity.
|
||||
"""
|
||||
GenericASTTraversal.__init__(self, ast=None)
|
||||
self.scanner = scanner
|
||||
params = {
|
||||
@@ -146,6 +212,10 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
self.line_number = 0
|
||||
self.ast_errors = []
|
||||
|
||||
# Sometimes we may want to continue decompiling when there are errors
|
||||
# and sometimes not
|
||||
self.tolerate_errors = tolerate_errors
|
||||
|
||||
# hide_internal suppresses displaying the additional instructions that sometimes
|
||||
# exist in code but but were not written in the source code.
|
||||
# An example is:
|
||||
@@ -213,47 +283,39 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
'DELETE_DEREF': ( '%{pattr}', 0 ),
|
||||
})
|
||||
|
||||
if version < 2.0:
|
||||
TABLE_DIRECT.update({
|
||||
'importlist': ( '%C', (0, maxint, ', ') ),
|
||||
})
|
||||
else:
|
||||
TABLE_DIRECT.update({
|
||||
'importlist2': ( '%C', (0, maxint, ', ') ),
|
||||
})
|
||||
if version <= 2.4:
|
||||
if version == 2.3:
|
||||
TABLE_DIRECT.update({
|
||||
'if1_stmt': ( '%|if 1\n%+%c%-', 5 )
|
||||
})
|
||||
|
||||
global NAME_MODULE
|
||||
NAME_MODULE = AST('stmt',
|
||||
[ AST('assign',
|
||||
[ AST('expr',
|
||||
[Token('LOAD_GLOBAL', pattr='__name__',
|
||||
offset=0, has_arg=True)]),
|
||||
AST('designator',
|
||||
[ Token('STORE_NAME', pattr='__module__',
|
||||
offset=3, has_arg=True)])
|
||||
])])
|
||||
pass
|
||||
if version <= 2.3:
|
||||
if version <= 2.4:
|
||||
if version == 2.3:
|
||||
TABLE_DIRECT.update({
|
||||
'tryfinallystmt': ( '%|try:\n%+%c%-%|finally:\n%+%c%-\n\n', 1, 4 )
|
||||
'if1_stmt': ( '%|if 1\n%+%c%-', 5 )
|
||||
})
|
||||
|
||||
elif version >= 2.5:
|
||||
########################
|
||||
# Import style for 2.5+
|
||||
########################
|
||||
TABLE_DIRECT.update({
|
||||
'importmultiple': ( '%|import %c%c\n', 2, 3 ),
|
||||
'import_cont' : ( ', %c', 2 ),
|
||||
# With/as is allowed as "from future" thing in 2.5
|
||||
'withstmt': ( '%|with %c:\n%+%c%-', 0, 3),
|
||||
'withasstmt': ( '%|with %c as %c:\n%+%c%-', 0, 2, 3),
|
||||
})
|
||||
global NAME_MODULE
|
||||
NAME_MODULE = AST('stmt',
|
||||
[ AST('assign',
|
||||
[ AST('expr',
|
||||
[Token('LOAD_GLOBAL', pattr='__name__',
|
||||
offset=0, has_arg=True)]),
|
||||
AST('designator',
|
||||
[ Token('STORE_NAME', pattr='__module__',
|
||||
offset=3, has_arg=True)])
|
||||
])])
|
||||
pass
|
||||
if version <= 2.3:
|
||||
TABLE_DIRECT.update({
|
||||
'tryfinallystmt': ( '%|try:\n%+%c%-%|finally:\n%+%c%-\n\n', 1, 4 )
|
||||
})
|
||||
|
||||
elif version >= 2.5:
|
||||
########################
|
||||
# Import style for 2.5+
|
||||
########################
|
||||
TABLE_DIRECT.update({
|
||||
'importmultiple': ( '%|import %c%c\n', 2, 3 ),
|
||||
'import_cont' : ( ', %c', 2 ),
|
||||
# With/as is allowed as "from future" thing in 2.5
|
||||
'withstmt': ( '%|with %c:\n%+%c%-', 0, 3),
|
||||
'withasstmt': ( '%|with %c as %c:\n%+%c%-', 0, 2, 3),
|
||||
})
|
||||
|
||||
########################################
|
||||
# Python 2.6+
|
||||
@@ -304,7 +366,7 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
# MAKE_FUNCTION ..
|
||||
code = node[-3]
|
||||
|
||||
self.indentMore()
|
||||
self.indent_more()
|
||||
for annotate_last in range(len(node)-1, -1, -1):
|
||||
if node[annotate_last] == 'annotate_tuple':
|
||||
break
|
||||
@@ -324,7 +386,7 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
self.write('\n\n')
|
||||
else:
|
||||
self.write('\n\n\n')
|
||||
self.indentLess()
|
||||
self.indent_less()
|
||||
self.prune() # stop recursing
|
||||
self.n_mkfunc_annotate = n_mkfunc_annotate
|
||||
|
||||
@@ -354,13 +416,20 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
# 'unmapexpr': ( '{**%c}', 0), # done by n_unmapexpr
|
||||
|
||||
})
|
||||
if version >= 3.6:
|
||||
TABLE_DIRECT.update({
|
||||
'trystmt36': ( '%|try:\n%+%c%-%c\n\n', 1, 2 ),
|
||||
})
|
||||
|
||||
def n_async_call_function(node):
|
||||
self.f.write('async ')
|
||||
node.type == 'call_function'
|
||||
node.kind == 'call_function'
|
||||
p = self.prec
|
||||
self.prec = 80
|
||||
self.engine(('%c(%P)', 0, (1, -4, ', ', 100)), node)
|
||||
self.template_engine(('%c(%P)', 0,
|
||||
(1, -4, ', ', 100)), node)
|
||||
self.prec = p
|
||||
node.kind == 'async_call_function'
|
||||
self.prune()
|
||||
self.n_async_call_function = n_async_call_function
|
||||
self.n_build_list_unpack = self.n_build_list
|
||||
@@ -373,13 +442,13 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
for i in mapping[1:]:
|
||||
key = key[i]
|
||||
pass
|
||||
if key.type.startswith('CALL_FUNCTION_VAR_KW'):
|
||||
if key.kind.startswith('CALL_FUNCTION_VAR_KW'):
|
||||
# Python 3.5 changes the stack position of *args. kwargs come
|
||||
# after *args whereas in earlier Pythons, *args is at the end
|
||||
# which simpilfiies things from our perspective.
|
||||
# Python 3.6+ replaces CALL_FUNCTION_VAR_KW with CALL_FUNCTION_EX
|
||||
# We will just swap the order to make it look like earlier Python 3.
|
||||
entry = table[key.type]
|
||||
entry = table[key.kind]
|
||||
kwarg_pos = entry[2][1]
|
||||
args_pos = kwarg_pos - 1
|
||||
# Put last node[args_pos] after subsequent kwargs
|
||||
@@ -400,9 +469,11 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
is_code = hasattr(code_node, 'attr') and iscode(code_node.attr)
|
||||
if (is_code and
|
||||
(code_node.attr.co_flags & COMPILER_FLAG_BIT['COROUTINE'])):
|
||||
self.engine(('\n\n%|async def %c\n', -2), node)
|
||||
self.template_engine(('\n\n%|async def %c\n',
|
||||
-2), node)
|
||||
else:
|
||||
self.engine(('\n\n%|def %c\n', -2), node)
|
||||
self.template_engine(('\n\n%|def %c\n', -2),
|
||||
node)
|
||||
self.prune()
|
||||
self.n_funcdef = n_funcdef
|
||||
|
||||
@@ -500,10 +571,10 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
super(SourceWalker, self).preorder(node)
|
||||
self.set_pos_info(node)
|
||||
|
||||
def indentMore(self, indent=TAB):
|
||||
def indent_more(self, indent=TAB):
|
||||
self.indent += indent
|
||||
|
||||
def indentLess(self, indent=TAB):
|
||||
def indent_less(self, indent=TAB):
|
||||
self.indent = self.indent[:-len(indent)]
|
||||
|
||||
def traverse(self, node, indent=None, isLambda=False):
|
||||
@@ -578,6 +649,20 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
node == AST('return_stmt',
|
||||
[AST('ret_expr', [NONE]), Token('RETURN_VALUE')]))
|
||||
|
||||
# Python 3.x can have be dead code as a result of its optimization?
|
||||
# So we'll add a # at the end of the return lambda so the rest is ignored
|
||||
def n_return_lambda(self, node):
|
||||
if 1 <= len(node) <= 2:
|
||||
self.preorder(node[0])
|
||||
self.write(' # Avoid dead code: ')
|
||||
self.prune()
|
||||
else:
|
||||
# We can't comment out like above because there may be a trailing ')'
|
||||
# that needs to be written
|
||||
assert len(node) == 3 and node[2] == 'LAMBDA_MARKER'
|
||||
self.preorder(node[0])
|
||||
self.prune()
|
||||
|
||||
def n_return_stmt(self, node):
|
||||
if self.params['isLambda']:
|
||||
self.preorder(node[0])
|
||||
@@ -595,6 +680,7 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
|
||||
def n_return_if_stmt(self, node):
|
||||
if self.params['isLambda']:
|
||||
self.write(' return ')
|
||||
self.preorder(node[0])
|
||||
self.prune()
|
||||
else:
|
||||
@@ -651,12 +737,12 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
|
||||
def n_expr(self, node):
|
||||
p = self.prec
|
||||
if node[0].type.startswith('binary_expr'):
|
||||
if node[0].kind.startswith('binary_expr'):
|
||||
n = node[0][-1][0]
|
||||
else:
|
||||
n = node[0]
|
||||
|
||||
self.prec = PRECEDENCE.get(n.type, -2)
|
||||
self.prec = PRECEDENCE.get(n.kind, -2)
|
||||
if n == 'LOAD_CONST' and repr(n.pattr)[0] == '-':
|
||||
self.prec = 6
|
||||
|
||||
@@ -739,9 +825,9 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
self.prune()
|
||||
|
||||
def n_delete_subscr(self, node):
|
||||
if node[-2][0] == 'build_list' and node[-2][0][-1].type.startswith('BUILD_TUPLE'):
|
||||
if node[-2][0] == 'build_list' and node[-2][0][-1].kind.startswith('BUILD_TUPLE'):
|
||||
if node[-2][0][-1] != 'BUILD_TUPLE_0':
|
||||
node[-2][0].type = 'build_tuple2'
|
||||
node[-2][0].kind = 'build_tuple2'
|
||||
self.default(node)
|
||||
|
||||
n_store_subscr = n_binary_subscr = n_delete_subscr
|
||||
@@ -750,9 +836,9 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
def n_tryfinallystmt(self, node):
|
||||
if len(node[1][0]) == 1 and node[1][0][0] == 'stmt':
|
||||
if node[1][0][0][0] == 'trystmt':
|
||||
node[1][0][0][0].type = 'tf_trystmt'
|
||||
node[1][0][0][0].kind = 'tf_trystmt'
|
||||
if node[1][0][0][0] == 'tryelsestmt':
|
||||
node[1][0][0][0].type = 'tf_tryelsestmt'
|
||||
node[1][0][0][0].kind = 'tf_tryelsestmt'
|
||||
self.default(node)
|
||||
|
||||
def n_exec_stmt(self, node):
|
||||
@@ -777,26 +863,26 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
|
||||
if len(n) == 1 == len(n[0]) and n[0] == '_stmts':
|
||||
n = n[0][0][0]
|
||||
elif n[0].type in ('lastc_stmt', 'lastl_stmt'):
|
||||
elif n[0].kind in ('lastc_stmt', 'lastl_stmt'):
|
||||
n = n[0][0]
|
||||
else:
|
||||
if not preprocess:
|
||||
self.default(node)
|
||||
return
|
||||
|
||||
if n.type in ('ifstmt', 'iflaststmt', 'iflaststmtl'):
|
||||
node.type = 'ifelifstmt'
|
||||
n.type = 'elifstmt'
|
||||
elif n.type in ('ifelsestmtr',):
|
||||
node.type = 'ifelifstmt'
|
||||
n.type = 'elifelsestmtr'
|
||||
elif n.type in ('ifelsestmt', 'ifelsestmtc', 'ifelsestmtl'):
|
||||
node.type = 'ifelifstmt'
|
||||
if n.kind in ('ifstmt', 'iflaststmt', 'iflaststmtl'):
|
||||
node.kind = 'ifelifstmt'
|
||||
n.kind = 'elifstmt'
|
||||
elif n.kind in ('ifelsestmtr',):
|
||||
node.kind = 'ifelifstmt'
|
||||
n.kind = 'elifelsestmtr'
|
||||
elif n.kind in ('ifelsestmt', 'ifelsestmtc', 'ifelsestmtl'):
|
||||
node.kind = 'ifelifstmt'
|
||||
self.n_ifelsestmt(n, preprocess=True)
|
||||
if n == 'ifelifstmt':
|
||||
n.type = 'elifelifstmt'
|
||||
elif n.type in ('ifelsestmt', 'ifelsestmtc', 'ifelsestmtl'):
|
||||
n.type = 'elifelsestmt'
|
||||
n.kind = 'elifelifstmt'
|
||||
elif n.kind in ('ifelsestmt', 'ifelsestmtc', 'ifelsestmtl'):
|
||||
n.kind = 'elifelsestmt'
|
||||
if not preprocess:
|
||||
self.default(node)
|
||||
|
||||
@@ -805,7 +891,7 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
def n_ifelsestmtr(self, node):
|
||||
if node[2] == 'COME_FROM':
|
||||
return_stmts_node = node[3]
|
||||
node.type = 'ifelsestmtr2'
|
||||
node.kind = 'ifelsestmtr2'
|
||||
else:
|
||||
return_stmts_node = node[2]
|
||||
if len(return_stmts_node) != 2:
|
||||
@@ -821,9 +907,9 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
self.write(self.indent, 'if ')
|
||||
self.preorder(node[0])
|
||||
self.println(':')
|
||||
self.indentMore()
|
||||
self.indent_more()
|
||||
self.preorder(node[1])
|
||||
self.indentLess()
|
||||
self.indent_less()
|
||||
|
||||
if_ret_at_end = False
|
||||
if len(return_stmts_node[0]) >= 3:
|
||||
@@ -836,27 +922,27 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
for n in return_stmts_node[0]:
|
||||
if (n[0] == 'ifstmt' and n[0][1][0] == 'return_if_stmts'):
|
||||
if prev_stmt_is_if_ret:
|
||||
n[0].type = 'elifstmt'
|
||||
n[0].kind = 'elifstmt'
|
||||
prev_stmt_is_if_ret = True
|
||||
else:
|
||||
prev_stmt_is_if_ret = False
|
||||
if not past_else and not if_ret_at_end:
|
||||
self.println(self.indent, 'else:')
|
||||
self.indentMore()
|
||||
self.indent_more()
|
||||
past_else = True
|
||||
self.preorder(n)
|
||||
if not past_else or if_ret_at_end:
|
||||
self.println(self.indent, 'else:')
|
||||
self.indentMore()
|
||||
self.indent_more()
|
||||
self.preorder(return_stmts_node[1])
|
||||
self.indentLess()
|
||||
self.indent_less()
|
||||
self.prune()
|
||||
n_ifelsestmtr2 = n_ifelsestmtr
|
||||
|
||||
def n_elifelsestmtr(self, node):
|
||||
if node[2] == 'COME_FROM':
|
||||
return_stmts_node = node[3]
|
||||
node.type = 'elifelsestmtr2'
|
||||
node.kind = 'elifelsestmtr2'
|
||||
else:
|
||||
return_stmts_node = node[2]
|
||||
|
||||
@@ -871,22 +957,22 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
self.write(self.indent, 'elif ')
|
||||
self.preorder(node[0])
|
||||
self.println(':')
|
||||
self.indentMore()
|
||||
self.indent_more()
|
||||
self.preorder(node[1])
|
||||
self.indentLess()
|
||||
self.indent_less()
|
||||
|
||||
for n in return_stmts_node[0]:
|
||||
n[0].type = 'elifstmt'
|
||||
n[0].kind = 'elifstmt'
|
||||
self.preorder(n)
|
||||
self.println(self.indent, 'else:')
|
||||
self.indentMore()
|
||||
self.indent_more()
|
||||
self.preorder(return_stmts_node[1])
|
||||
self.indentLess()
|
||||
self.indent_less()
|
||||
self.prune()
|
||||
|
||||
def n_import_as(self, node):
|
||||
store_node = node[-1][-1]
|
||||
assert store_node.type.startswith('STORE_')
|
||||
assert store_node.kind.startswith('STORE_')
|
||||
iname = node[0].pattr # import name
|
||||
sname = store_node.pattr # store_name
|
||||
if iname and iname == sname or iname.startswith(sname + '.'):
|
||||
@@ -922,14 +1008,14 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
func_name = code_node.attr.co_name
|
||||
self.write(func_name)
|
||||
|
||||
self.indentMore()
|
||||
self.indent_more()
|
||||
self.make_function(node, isLambda=False, codeNode=code_node)
|
||||
|
||||
if len(self.param_stack) > 1:
|
||||
self.write('\n\n')
|
||||
else:
|
||||
self.write('\n\n\n')
|
||||
self.indentLess()
|
||||
self.indent_less()
|
||||
self.prune() # stop recursing
|
||||
|
||||
def make_function(self, node, isLambda, nested=1,
|
||||
@@ -1006,7 +1092,7 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
"""
|
||||
p = self.prec
|
||||
self.prec = 27
|
||||
if node[-1].type == 'list_iter':
|
||||
if node[-1].kind == 'list_iter':
|
||||
n = node[-1]
|
||||
elif self.is_pypy and node[-1] == 'JUMP_BACK':
|
||||
n = node[-2]
|
||||
@@ -1130,7 +1216,7 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
self.write('{')
|
||||
if node[0] in ['LOAD_SETCOMP', 'LOAD_DICTCOMP']:
|
||||
self.comprehension_walk3(node, 1, 0)
|
||||
elif node[0].type == 'load_closure' and self.version >= 3.0:
|
||||
elif node[0].kind == 'load_closure' and self.version >= 3.0:
|
||||
self.setcomprehension_walk3(node, collection_index=4)
|
||||
else:
|
||||
self.comprehension_walk(node, iter_index=4)
|
||||
@@ -1197,7 +1283,7 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
|
||||
# Python 2.7+ starts including set_comp_body
|
||||
# Python 3.5+ starts including setcomp_func
|
||||
assert n.type in ('lc_body', 'comp_body', 'setcomp_func', 'set_comp_body'), ast
|
||||
assert n.kind in ('lc_body', 'comp_body', 'setcomp_func', 'set_comp_body'), ast
|
||||
assert designator, "Couldn't find designator in list/set comprehension"
|
||||
|
||||
self.preorder(n[0])
|
||||
@@ -1247,7 +1333,7 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
n = n[3]
|
||||
elif n in ('list_if', 'list_if_not'):
|
||||
# FIXME: just a guess
|
||||
if n[0].type == 'expr':
|
||||
if n[0].kind == 'expr':
|
||||
list_if = n
|
||||
else:
|
||||
list_if = n[1]
|
||||
@@ -1268,7 +1354,7 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
|
||||
def n_listcomp(self, node):
|
||||
self.write('[')
|
||||
if node[0].type == 'load_closure':
|
||||
if node[0].kind == 'load_closure':
|
||||
self.listcomprehension_walk2(node)
|
||||
else:
|
||||
self.comprehension_walk3(node, 1, 0)
|
||||
@@ -1305,7 +1391,7 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
n = n[3]
|
||||
elif n in ('list_if', 'list_if_not', 'comp_if', 'comp_if_not'):
|
||||
# FIXME: just a guess
|
||||
if n[0].type == 'expr':
|
||||
if n[0].kind == 'expr':
|
||||
list_if = n
|
||||
else:
|
||||
list_if = n[1]
|
||||
@@ -1421,9 +1507,9 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
self.println(':')
|
||||
|
||||
# class body
|
||||
self.indentMore()
|
||||
self.indent_more()
|
||||
self.build_class(subclass_code)
|
||||
self.indentLess()
|
||||
self.indent_less()
|
||||
|
||||
self.currentclass = cclass
|
||||
if len(self.param_stack) > 1:
|
||||
@@ -1440,7 +1526,7 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
return
|
||||
|
||||
n_subclasses = len(node[:-1])
|
||||
if n_subclasses > 0 or self.version > 2.1:
|
||||
if n_subclasses > 0 or self.version > 2.4:
|
||||
# Not an old-style pre-2.2 class
|
||||
self.write('(')
|
||||
|
||||
@@ -1451,16 +1537,16 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
self.write(sep, value)
|
||||
sep = line_separator
|
||||
|
||||
if n_subclasses > 0 or self.version > 2.1:
|
||||
if n_subclasses > 0 or self.version > 2.4:
|
||||
# Not an old-style pre-2.2 class
|
||||
self.write(')')
|
||||
|
||||
def print_super_classes3(self, node):
|
||||
n = len(node)-1
|
||||
if node.type != 'expr':
|
||||
assert node[n].type.startswith('CALL_FUNCTION')
|
||||
if node.kind != 'expr':
|
||||
assert node[n].kind.startswith('CALL_FUNCTION')
|
||||
for i in range(n-2, 0, -1):
|
||||
if not node[i].type in ['expr', 'LOAD_CLASSNAME']:
|
||||
if not node[i].kind in ['expr', 'LOAD_CLASSNAME']:
|
||||
break
|
||||
pass
|
||||
|
||||
@@ -1494,13 +1580,13 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
p = self.prec
|
||||
self.prec = 100
|
||||
|
||||
self.indentMore(INDENT_PER_LEVEL)
|
||||
self.indent_more(INDENT_PER_LEVEL)
|
||||
sep = INDENT_PER_LEVEL[:-1]
|
||||
self.write('{')
|
||||
line_number = self.line_number
|
||||
|
||||
if self.version >= 3.0 and not self.is_pypy:
|
||||
if node[0].type.startswith('kvlist'):
|
||||
if node[0].kind.startswith('kvlist'):
|
||||
# Python 3.5+ style key/value list in mapexpr
|
||||
kv_node = node[0]
|
||||
l = list(kv_node)
|
||||
@@ -1523,11 +1609,11 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
i += 2
|
||||
pass
|
||||
pass
|
||||
elif len(node) > 1 and node[1].type.startswith('kvlist'):
|
||||
elif len(node) > 1 and node[1].kind.startswith('kvlist'):
|
||||
# Python 3.0..3.4 style key/value list in mapexpr
|
||||
kv_node = node[1]
|
||||
l = list(kv_node)
|
||||
if len(l) > 0 and l[0].type == 'kv3':
|
||||
if len(l) > 0 and l[0].kind == 'kv3':
|
||||
# Python 3.2 does this
|
||||
kv_node = node[1][0]
|
||||
l = list(kv_node)
|
||||
@@ -1552,7 +1638,7 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
i += 3
|
||||
pass
|
||||
pass
|
||||
elif node[-1].type.startswith('BUILD_CONST_KEY_MAP'):
|
||||
elif node[-1].kind.startswith('BUILD_CONST_KEY_MAP'):
|
||||
# Python 3.6+ style const map
|
||||
keys = node[-2].pattr
|
||||
values = node[:-2]
|
||||
@@ -1577,7 +1663,7 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
pass
|
||||
else:
|
||||
# Python 2 style kvlist
|
||||
assert node[-1].type.startswith('kvlist')
|
||||
assert node[-1].kind.startswith('kvlist')
|
||||
kv_node = node[-1] # goto kvlist
|
||||
|
||||
first_time = True
|
||||
@@ -1632,7 +1718,7 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
if sep.startswith(",\n"):
|
||||
self.write(sep[1:])
|
||||
self.write('}')
|
||||
self.indentLess(INDENT_PER_LEVEL)
|
||||
self.indent_less(INDENT_PER_LEVEL)
|
||||
self.prec = p
|
||||
self.prune()
|
||||
|
||||
@@ -1643,7 +1729,7 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
p = self.prec
|
||||
self.prec = 100
|
||||
lastnode = node.pop()
|
||||
lastnodetype = lastnode.type
|
||||
lastnodetype = lastnode.kind
|
||||
|
||||
# If this build list is inside a CALL_FUNCTION_VAR,
|
||||
# then the first * has already been printed.
|
||||
@@ -1683,7 +1769,7 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
else:
|
||||
flat_elems.append(elem)
|
||||
|
||||
self.indentMore(INDENT_PER_LEVEL)
|
||||
self.indent_more(INDENT_PER_LEVEL)
|
||||
sep = ''
|
||||
|
||||
for elem in flat_elems:
|
||||
@@ -1708,12 +1794,12 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
if lastnode.attr == 1 and lastnodetype.startswith('BUILD_TUPLE'):
|
||||
self.write(',')
|
||||
self.write(endchar)
|
||||
self.indentLess(INDENT_PER_LEVEL)
|
||||
self.indent_less(INDENT_PER_LEVEL)
|
||||
self.prec = p
|
||||
self.prune()
|
||||
|
||||
def n_unpack(self, node):
|
||||
if node[0].type.startswith('UNPACK_EX'):
|
||||
if node[0].kind.startswith('UNPACK_EX'):
|
||||
# Python 3+
|
||||
before_count, after_count = node[0].attr
|
||||
for i in range(before_count+1):
|
||||
@@ -1728,8 +1814,8 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
self.prune()
|
||||
return
|
||||
for n in node[1:]:
|
||||
if n[0].type == 'unpack':
|
||||
n[0].type = 'unpack_w_parens'
|
||||
if n[0].kind == 'unpack':
|
||||
n[0].kind = 'unpack_w_parens'
|
||||
self.default(node)
|
||||
|
||||
n_unpack_w_parens = n_unpack
|
||||
@@ -1738,33 +1824,38 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
# A horrible hack for Python 3.0 .. 3.2
|
||||
if 3.0 <= self.version <= 3.2 and len(node) == 2:
|
||||
if (node[0][0] == 'LOAD_FAST' and node[0][0].pattr == '__locals__' and
|
||||
node[1][0].type == 'STORE_LOCALS'):
|
||||
node[1][0].kind == 'STORE_LOCALS'):
|
||||
self.prune()
|
||||
self.default(node)
|
||||
|
||||
def n_assign2(self, node):
|
||||
for n in node[-2:]:
|
||||
if n[0] == 'unpack':
|
||||
n[0].type = 'unpack_w_parens'
|
||||
n[0].kind = 'unpack_w_parens'
|
||||
self.default(node)
|
||||
|
||||
def n_assign3(self, node):
|
||||
for n in node[-3:]:
|
||||
if n[0] == 'unpack':
|
||||
n[0].type = 'unpack_w_parens'
|
||||
n[0].kind = 'unpack_w_parens'
|
||||
self.default(node)
|
||||
|
||||
def n_except_cond2(self, node):
|
||||
if node[-2][0] == 'unpack':
|
||||
node[-2][0].type = 'unpack_w_parens'
|
||||
node[-2][0].kind = 'unpack_w_parens'
|
||||
self.default(node)
|
||||
|
||||
def engine(self, entry, startnode):
|
||||
def template_engine(self, entry, startnode):
|
||||
"""The format template interpetation engine. See the comment at the
|
||||
beginning of this module for the how we interpret format specifications such as
|
||||
%c, %C, and so on.
|
||||
beginning of this module for the how we interpret format
|
||||
specifications such as %c, %C, and so on.
|
||||
"""
|
||||
# self.println("----> ", startnode.type, ', ', entry[0])
|
||||
|
||||
# print("-----")
|
||||
# print(startnode)
|
||||
# print(entry[0])
|
||||
# print('======')
|
||||
|
||||
fmt = entry[0]
|
||||
arg = 1
|
||||
i = 0
|
||||
@@ -1782,24 +1873,30 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
if typ == '%': self.write('%')
|
||||
elif typ == '+':
|
||||
self.line_number += 1
|
||||
self.indentMore()
|
||||
self.indent_more()
|
||||
elif typ == '-':
|
||||
self.line_number += 1
|
||||
self.indentLess()
|
||||
self.indent_less()
|
||||
elif typ == '|':
|
||||
self.line_number += 1
|
||||
self.write(self.indent)
|
||||
# Used mostly on the LHS of an assignment
|
||||
# BUILD_TUPLE_n is pretty printed and may take care of other uses.
|
||||
elif typ == ',':
|
||||
if (node.type in ('unpack', 'unpack_w_parens') and
|
||||
if (node.kind in ('unpack', 'unpack_w_parens') and
|
||||
node[0].attr == 1):
|
||||
self.write(',')
|
||||
elif typ == 'c':
|
||||
if isinstance(entry[arg], int):
|
||||
entry_node = node[entry[arg]]
|
||||
self.preorder(entry_node)
|
||||
arg += 1
|
||||
index = entry[arg]
|
||||
if isinstance(index, tuple):
|
||||
assert node[index[0]] == index[1], (
|
||||
"at %s[%d], %s vs %s" % (
|
||||
node.kind, arg, node[index[0]].kind, index[1])
|
||||
)
|
||||
index = index[0]
|
||||
if isinstance(index, int):
|
||||
self.preorder(node[index])
|
||||
arg += 1
|
||||
elif typ == 'p':
|
||||
p = self.prec
|
||||
(index, self.prec) = entry[arg]
|
||||
@@ -1865,8 +1962,8 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
key = key[i]
|
||||
pass
|
||||
|
||||
if key.type in table:
|
||||
self.engine(table[key.type], node)
|
||||
if key.kind in table:
|
||||
self.template_engine(table[key.kind], node)
|
||||
self.prune()
|
||||
|
||||
def customize(self, customize):
|
||||
@@ -1890,7 +1987,7 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
'CALL_FUNCTION_VAR_KW', 'CALL_FUNCTION_KW'):
|
||||
if v == 0:
|
||||
str = '%c(%C' # '%C' is a dummy here ...
|
||||
p2 = (0, 0, None) # .. because of this
|
||||
p2 = (0, 0, None) # .. because of the None in this
|
||||
else:
|
||||
str = '%c(%C, '
|
||||
p2 = (1, -2, ', ')
|
||||
@@ -2085,6 +2182,11 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
# assert isinstance(tokens[0], Token)
|
||||
|
||||
if isLambda:
|
||||
for t in tokens:
|
||||
if t.kind == 'RETURN_END_IF':
|
||||
t.kind = 'RETURN_END_IF_LAMBDA'
|
||||
elif t.kind == 'RETURN_VALUE':
|
||||
t.kind = 'RETURN_VALUE_LAMBDA'
|
||||
tokens.append(Token('LAMBDA_MARKER'))
|
||||
try:
|
||||
ast = python_parser.parse(self.p, tokens, customize)
|
||||
@@ -2101,10 +2203,10 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
# than fight (with the grammar to not emit "return None").
|
||||
if self.hide_internal:
|
||||
if len(tokens) >= 2 and not noneInNames:
|
||||
if tokens[-1].type == 'RETURN_VALUE':
|
||||
if tokens[-1].kind in ('RETURN_VALUE', 'RETURN_VALUE_LAMBDA'):
|
||||
# Python 3.4's classes can add a "return None" which is
|
||||
# invalid syntax.
|
||||
if tokens[-2].type == 'LOAD_CONST':
|
||||
if tokens[-2].kind == 'LOAD_CONST':
|
||||
if isTopLevel or tokens[-2].pattr is None:
|
||||
del tokens[-2:]
|
||||
else:
|
||||
@@ -2148,7 +2250,7 @@ def deparse_code(version, co, out=sys.stdout, showasm=None, showast=False,
|
||||
debug_parser = dict(PARSER_DEFAULT_DEBUG)
|
||||
if showgrammar:
|
||||
debug_parser['reduce'] = showgrammar
|
||||
debug_parser['errorstack'] = True
|
||||
debug_parser['errorstack'] = 'full'
|
||||
|
||||
# Build AST from disassembly.
|
||||
linestarts = dict(scanner.opc.findlinestarts(co))
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#
|
||||
# (C) Copyright 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
||||
# (C) Copyright 2015-2016 by Rocky Bernstein
|
||||
# (C) Copyright 2015-2017 by Rocky Bernstein
|
||||
#
|
||||
"""
|
||||
byte-code verification
|
||||
@@ -43,7 +43,7 @@ BIN_OP_FUNCS = {
|
||||
'BINARY_OR': operator.or_,
|
||||
}
|
||||
|
||||
JUMP_OPs = None
|
||||
JUMP_OPS = None
|
||||
|
||||
# --- exceptions ---
|
||||
|
||||
@@ -225,8 +225,8 @@ def cmp_code_objects(version, is_pypy, code_obj1, code_obj2,
|
||||
import uncompyle6.scanners.scanner36 as scan
|
||||
scanner = scan.Scanner36()
|
||||
|
||||
global JUMP_OPs
|
||||
JUMP_OPs = list(scan.JUMP_OPs) + ['JUMP_BACK']
|
||||
global JUMP_OPS
|
||||
JUMP_OPS = list(scan.JUMP_OPS) + ['JUMP_BACK']
|
||||
|
||||
# use changed Token class
|
||||
# We (re)set this here to save exception handling,
|
||||
@@ -242,18 +242,18 @@ def cmp_code_objects(version, is_pypy, code_obj1, code_obj2,
|
||||
scanner.resetTokenClass() # restore Token class
|
||||
|
||||
targets1 = dis.findlabels(code_obj1.co_code)
|
||||
tokens1 = [t for t in tokens1 if t.type != 'COME_FROM']
|
||||
tokens2 = [t for t in tokens2 if t.type != 'COME_FROM']
|
||||
tokens1 = [t for t in tokens1 if t.kind != 'COME_FROM']
|
||||
tokens2 = [t for t in tokens2 if t.kind != 'COME_FROM']
|
||||
|
||||
i1 = 0; i2 = 0
|
||||
offset_map = {}; check_jumps = {}
|
||||
while i1 < len(tokens1):
|
||||
if i2 >= len(tokens2):
|
||||
if len(tokens1) == len(tokens2) + 2 \
|
||||
and tokens1[-1].type == 'RETURN_VALUE' \
|
||||
and tokens1[-2].type == 'LOAD_CONST' \
|
||||
and tokens1[-1].kind == 'RETURN_VALUE' \
|
||||
and tokens1[-2].kind == 'LOAD_CONST' \
|
||||
and tokens1[-2].pattr is None \
|
||||
and tokens1[-3].type == 'RETURN_VALUE':
|
||||
and tokens1[-3].kind == 'RETURN_VALUE':
|
||||
break
|
||||
else:
|
||||
raise CmpErrorCodeLen(name, tokens1, tokens2)
|
||||
@@ -265,13 +265,13 @@ def cmp_code_objects(version, is_pypy, code_obj1, code_obj2,
|
||||
raise CmpErrorCode(name, tokens1[idx1].offset, tokens1[idx1],
|
||||
tokens2[idx2], tokens1, tokens2)
|
||||
|
||||
if tokens1[i1].type != tokens2[i2].type:
|
||||
if tokens1[i1].type == 'LOAD_CONST' == tokens2[i2].type:
|
||||
if tokens1[i1].kind != tokens2[i2].kind:
|
||||
if tokens1[i1].kind == 'LOAD_CONST' == tokens2[i2].kind:
|
||||
i = 1
|
||||
while tokens1[i1+i].type == 'LOAD_CONST':
|
||||
while tokens1[i1+i].kind == 'LOAD_CONST':
|
||||
i += 1
|
||||
if tokens1[i1+i].type.startswith(('BUILD_TUPLE', 'BUILD_LIST')) \
|
||||
and i == int(tokens1[i1+i].type.split('_')[-1]):
|
||||
if tokens1[i1+i].kind.startswith(('BUILD_TUPLE', 'BUILD_LIST')) \
|
||||
and i == int(tokens1[i1+i].kind.split('_')[-1]):
|
||||
t = tuple([ elem.pattr for elem in tokens1[i1:i1+i] ])
|
||||
if t != tokens2[i2].pattr:
|
||||
raise CmpErrorCode(name, tokens1[i1].offset, tokens1[i1],
|
||||
@@ -279,60 +279,65 @@ def cmp_code_objects(version, is_pypy, code_obj1, code_obj2,
|
||||
i1 += i + 1
|
||||
i2 += 1
|
||||
continue
|
||||
elif i == 2 and tokens1[i1+i].type == 'ROT_TWO' and tokens2[i2+1].type == 'UNPACK_SEQUENCE_2':
|
||||
elif i == 2 and tokens1[i1+i].kind == 'ROT_TWO' and tokens2[i2+1].kind == 'UNPACK_SEQUENCE_2':
|
||||
i1 += 3
|
||||
i2 += 2
|
||||
continue
|
||||
elif i == 2 and tokens1[i1+i].type in BIN_OP_FUNCS:
|
||||
f = BIN_OP_FUNCS[tokens1[i1+i].type]
|
||||
elif i == 2 and tokens1[i1+i].kind in BIN_OP_FUNCS:
|
||||
f = BIN_OP_FUNCS[tokens1[i1+i].kind]
|
||||
if f(tokens1[i1].pattr, tokens1[i1+1].pattr) == tokens2[i2].pattr:
|
||||
i1 += 3
|
||||
i2 += 1
|
||||
continue
|
||||
elif tokens1[i1].type == 'UNARY_NOT':
|
||||
if tokens2[i2].type == 'POP_JUMP_IF_TRUE':
|
||||
if tokens1[i1+1].type == 'POP_JUMP_IF_FALSE':
|
||||
elif tokens1[i1].kind == 'UNARY_NOT':
|
||||
if tokens2[i2].kind == 'POP_JUMP_IF_TRUE':
|
||||
if tokens1[i1+1].kind == 'POP_JUMP_IF_FALSE':
|
||||
i1 += 2
|
||||
i2 += 1
|
||||
continue
|
||||
elif tokens2[i2].type == 'POP_JUMP_IF_FALSE':
|
||||
if tokens1[i1+1].type == 'POP_JUMP_IF_TRUE':
|
||||
elif tokens2[i2].kind == 'POP_JUMP_IF_FALSE':
|
||||
if tokens1[i1+1].kind == 'POP_JUMP_IF_TRUE':
|
||||
i1 += 2
|
||||
i2 += 1
|
||||
continue
|
||||
elif tokens1[i1].type in ('JUMP_FORWARD', 'JUMP_BACK') \
|
||||
and tokens1[i1-1].type == 'RETURN_VALUE' \
|
||||
and tokens2[i2-1].type in ('RETURN_VALUE', 'RETURN_END_IF') \
|
||||
elif tokens1[i1].kind in ('JUMP_FORWARD', 'JUMP_BACK') \
|
||||
and tokens1[i1-1].kind == 'RETURN_VALUE' \
|
||||
and tokens2[i2-1].kind in ('RETURN_VALUE', 'RETURN_END_IF') \
|
||||
and int(tokens1[i1].offset) not in targets1:
|
||||
i1 += 1
|
||||
continue
|
||||
elif tokens1[i1].type == 'JUMP_FORWARD' and tokens2[i2].type == 'JUMP_BACK' \
|
||||
and tokens1[i1+1].type == 'JUMP_BACK' and tokens2[i2+1].type == 'JUMP_BACK' \
|
||||
elif tokens1[i1].kind == 'JUMP_BACK' and tokens2[i2].kind == 'CONTINUE':
|
||||
# FIXME: should make sure that offset is inside loop, not outside of it
|
||||
i1 += 2
|
||||
i2 += 2
|
||||
continue
|
||||
elif tokens1[i1].kind == 'JUMP_FORWARD' and tokens2[i2].kind == 'JUMP_BACK' \
|
||||
and tokens1[i1+1].kind == 'JUMP_BACK' and tokens2[i2+1].kind == 'JUMP_BACK' \
|
||||
and int(tokens1[i1].pattr) == int(tokens1[i1].offset) + 3:
|
||||
if int(tokens1[i1].pattr) == int(tokens1[i1+1].offset):
|
||||
i1 += 2
|
||||
i2 += 2
|
||||
continue
|
||||
elif tokens1[i1].type == 'LOAD_NAME' and tokens2[i2].type == 'LOAD_CONST' \
|
||||
elif tokens1[i1].kind == 'LOAD_NAME' and tokens2[i2].kind == 'LOAD_CONST' \
|
||||
and tokens1[i1].pattr == 'None' and tokens2[i2].pattr is None:
|
||||
pass
|
||||
elif tokens1[i1].type == 'LOAD_GLOBAL' and tokens2[i2].type == 'LOAD_NAME' \
|
||||
elif tokens1[i1].kind == 'LOAD_GLOBAL' and tokens2[i2].kind == 'LOAD_NAME' \
|
||||
and tokens1[i1].pattr == tokens2[i2].pattr:
|
||||
pass
|
||||
elif tokens1[i1].type == 'LOAD_ASSERT' and tokens2[i2].type == 'LOAD_NAME' \
|
||||
elif tokens1[i1].kind == 'LOAD_ASSERT' and tokens2[i2].kind == 'LOAD_NAME' \
|
||||
and tokens1[i1].pattr == tokens2[i2].pattr:
|
||||
pass
|
||||
elif (tokens1[i1].type == 'RETURN_VALUE' and
|
||||
tokens2[i2].type == 'RETURN_END_IF'):
|
||||
elif (tokens1[i1].kind == 'RETURN_VALUE' and
|
||||
tokens2[i2].kind == 'RETURN_END_IF'):
|
||||
pass
|
||||
elif (tokens1[i1].type == 'BUILD_TUPLE_0' and
|
||||
elif (tokens1[i1].kind == 'BUILD_TUPLE_0' and
|
||||
tokens2[i2].pattr == ()):
|
||||
pass
|
||||
else:
|
||||
raise CmpErrorCode(name, tokens1[i1].offset, tokens1[i1],
|
||||
tokens2[i2], tokens1, tokens2)
|
||||
elif tokens1[i1].type in JUMP_OPs and tokens1[i1].pattr != tokens2[i2].pattr:
|
||||
if tokens1[i1].type == 'JUMP_BACK':
|
||||
elif tokens1[i1].kind in JUMP_OPS and tokens1[i1].pattr != tokens2[i2].pattr:
|
||||
if tokens1[i1].kind == 'JUMP_BACK':
|
||||
dest1 = int(tokens1[i1].pattr)
|
||||
dest2 = int(tokens2[i2].pattr)
|
||||
if offset_map[dest1] != dest2:
|
||||
@@ -387,28 +392,28 @@ def cmp_code_objects(version, is_pypy, code_obj1, code_obj2,
|
||||
class Token(scanner.Token):
|
||||
"""Token class with changed semantics for 'cmp()'."""
|
||||
def __cmp__(self, o):
|
||||
t = self.type # shortcut
|
||||
if t == 'BUILD_TUPLE_0' and o.type == 'LOAD_CONST' and o.pattr == ():
|
||||
t = self.kind # shortcut
|
||||
if t == 'BUILD_TUPLE_0' and o.kind == 'LOAD_CONST' and o.pattr == ():
|
||||
return 0
|
||||
if t == 'COME_FROM' == o.type:
|
||||
if t == 'COME_FROM' == o.kind:
|
||||
return 0
|
||||
if t == 'PRINT_ITEM_CONT' and o.type == 'PRINT_ITEM':
|
||||
if t == 'PRINT_ITEM_CONT' and o.kind == 'PRINT_ITEM':
|
||||
return 0
|
||||
if t == 'RETURN_VALUE' and o.type == 'RETURN_END_IF':
|
||||
if t == 'RETURN_VALUE' and o.kind == 'RETURN_END_IF':
|
||||
return 0
|
||||
if t == 'JUMP_IF_FALSE_OR_POP' and o.type == 'POP_JUMP_IF_FALSE':
|
||||
if t == 'JUMP_IF_FALSE_OR_POP' and o.kind == 'POP_JUMP_IF_FALSE':
|
||||
return 0
|
||||
if JUMP_OPs and t in JUMP_OPs:
|
||||
if JUMP_OPS and t in JUMP_OPS:
|
||||
# ignore offset
|
||||
return t == o.type
|
||||
return (t == o.type) or self.pattr == o.pattr
|
||||
return t == o.kind
|
||||
return (t == o.kind) or self.pattr == o.pattr
|
||||
|
||||
def __repr__(self):
|
||||
return '%s %s (%s)' % (str(self.type), str(self.attr),
|
||||
return '%s %s (%s)' % (str(self.kind), str(self.attr),
|
||||
repr(self.pattr))
|
||||
|
||||
def __str__(self):
|
||||
return '%s\t%-17s %r' % (self.offset, self.type, self.pattr)
|
||||
return '%s\t%-17s %r' % (self.offset, self.kind, self.pattr)
|
||||
|
||||
def compare_code_with_srcfile(pyc_filename, src_filename, weak_verify=False):
|
||||
"""Compare a .pyc with a source code file."""
|
||||
@@ -442,4 +447,4 @@ if __name__ == '__main__':
|
||||
t2 = Token('LOAD_CONST', -421, 'code_object _expandLang', 55)
|
||||
print(repr(t1))
|
||||
print(repr(t2))
|
||||
print(t1.type == t2.type, t1.attr == t2.attr)
|
||||
print(t1.kind == t2.kind, t1.attr == t2.attr)
|
||||
|
@@ -1,3 +1,3 @@
|
||||
# This file is suitable for sourcing inside bash as
|
||||
# well as importing into Python
|
||||
VERSION='2.11.5'
|
||||
VERSION='2.13.3'
|
||||
|
Reference in New Issue
Block a user