Compare commits

..

50 Commits

Author SHA1 Message Date
rocky
0f489672b9 More merge fixups from master 2017-06-18 16:05:22 -04:00
rocky
b7d8cbfaf5 Merge branch 'master' into python-2.4 2017-06-18 15:40:40 -04:00
rocky
df8d253f78 2.4 doesn't do six 2017-06-03 06:00:47 -04:00
rocky
89b42e3696 Nope it (appveyor) doesn't. 2017-06-03 05:55:21 -04:00
rocky
22e5a4a283 Administrivia
See if appveyor will handle 2.5
2017-06-03 05:53:41 -04:00
rocky
61810172d1 Merge branch 'master' into python-2.4 2017-06-03 05:50:42 -04:00
rocky
658c8b4be7 No decorators in Python < 2.6 2017-05-30 02:30:56 -04:00
rocky
d4dab54c7b Merge branch 'master' into python-2.4 2017-05-30 02:18:57 -04:00
rocky
5566b9ba6c Get ready for release 2.9.11 2017-05-06 07:49:09 -04:00
rocky
e56ab2dcd5 Sync with master 2017-05-06 07:17:04 -04:00
rocky
d6c45979ba Merge branch 'master' into python-2.4 2017-05-06 07:16:39 -04:00
rocky
a06e9bf32e Merge branch 'master' into python-2.4 2017-04-14 05:45:53 -04:00
rocky
7e8f7ba674 namedtuple25 -> namedtuple24 2017-04-14 05:42:44 -04:00
rocky
09eb7f7f78 Merge branch 'master' into python-2.4 2017-04-10 00:48:04 -04:00
rocky
f7a910ec66 Merge branch 'master' into python-2.4 2017-03-01 05:55:26 -05:00
rocky
6d6a73eea7 Merge branch 'master' into python-2.4 2017-02-25 21:02:12 -05:00
rocky
e4a7641927 Python <= 2.6 grammar fixes 2017-02-25 05:13:19 -05:00
rocky
b24b46d48c Merge branch 'master' into python-2.4 2017-02-25 04:48:06 -05:00
rocky
a65d7dce5b Python 2.5 was missing try else stmt 2017-02-22 05:30:07 -05:00
rocky
718a0a5d34 Merge branch 'master' into python-2.4 2017-02-22 05:29:49 -05:00
rocky
ea9e3ab3f5 Group coverage Makefile targets 2017-02-10 01:00:26 -05:00
rocky
770e988ff8 Changes based on coverage information 2017-01-29 22:54:30 -05:00
rocky
0fa0641974 Merge branch 'master' into python-2.4 2017-01-29 22:05:55 -05:00
rocky
c13e23cdae Get ready for release 2.9.9 2017-01-11 21:52:20 -05:00
rocky
fab4ebb768 Merge changes ...
* str() in Python 2.4 doesn't detect unicode.
* index() doesn't work on tuples
* ifelse change
2017-01-11 19:34:28 -05:00
rocky
89429339fa Merge branch 'master' into python-2.4 2017-01-11 19:25:44 -05:00
rocky
6ed129bd7a 2.4 verify hacks 2017-01-02 07:15:46 -05:00
rocky
c4fde6b53e Merge branch 'master' into python-2.4 2017-01-02 05:39:50 -05:00
rocky
a7d93e88b4 Merge branch 'master' into python-2.4 2017-01-02 05:39:13 -05:00
rocky
9891494142 We are version 2.9.9 2016-12-31 18:16:23 -05:00
rocky
f8544dfbbe 2.7->2.4 conversion 2016-12-31 10:56:43 -05:00
rocky
b00651d428 Merge master branche
Handle 2.2 list_if
2016-12-31 05:19:21 -05:00
rocky
da8dccbaca Merge branch 'master' into python-2.4 2016-12-29 02:08:12 -05:00
rocky
37272ae827 Merge commit '9b1dd0f' into python-2.4 2016-12-27 10:32:25 -05:00
rocky
7f2bee46b7 Bug in using python2 ast checking in python 2.5 2016-12-26 01:55:16 -05:00
rocky
c8a4dcf72b Removing NAME_MODULE, lint and bug fixes
scanner*.py: show_asm param is optional
verify.py: call correct scanners
main.py, verify.py: Use older Python print statements
2016-12-25 09:16:04 -05:00
rocky
012ff91cfb Merge branch 'master' into python-2.4 2016-12-25 07:57:17 -05:00
rocky
e690ddd50a Merge branch 'master' into python-2.4 2016-12-18 07:43:15 -05:00
rocky
45b7c1948c show-asm on python2.5 is optional
Make scanner2 a little more like scanner3.
2016-12-17 07:57:31 -05:00
rocky
e2fb7ca3d2 Python 2.6/2.7 tolerance in Python 2.4 branch 2016-12-17 06:51:47 -05:00
rocky
b3bda76582 Merge branch 'master' into python-2.4 2016-12-16 22:56:07 -05:00
rocky
ab6d322eca Get ready for release 2.9.7 2016-12-04 14:09:53 -05:00
rocky
1a8a0df107 Merge branch 'master' into python-2.4 2016-12-04 13:40:06 -05:00
rocky
0a37709b0a CircleCI build 2016-11-24 05:41:31 -05:00
rocky
98cd1417df Remove dup Python 3 grammar rule 2016-11-24 05:36:43 -05:00
rocky
460069ceaa Bug in 2.4 "if" dectection and...
Wrong language used in old-style exceptions: use "except Error,e" not
"except Error(e)""
2016-11-24 05:15:35 -05:00
rocky
316aa44f23 Python 2.6 grammary bug and..
__pkginfo.py__: Bump spark_parser version for parse_flags 'dups'
2016-11-24 04:09:32 -05:00
rocky
7133540c23 Make work on 2.4 2016-11-23 08:26:12 -05:00
rocky
590231741d Merge branch 'come-from-type' into python-2.4 2016-11-23 07:54:18 -05:00
rocky
a9349b8f3d Making it run on Python 2.4 and 2.5 2016-11-23 07:53:51 -05:00
87 changed files with 1262 additions and 2372 deletions

3
.gitignore vendored
View File

@@ -17,5 +17,4 @@
__pycache__
build
/.venv*
/.idea
/.hypothesis
/.idea

View File

@@ -3,13 +3,7 @@ language: python
sudo: false
python:
- '3.5'
- '2.7.12'
- '2.6'
- '3.3'
- '3.4'
- '3.2'
- '3.6'
- '2.7' # this is a cheat here because travis doesn't do 2.4-2.6
install:
- pip install -e .

655
ChangeLog
View File

@@ -1,422 +1,19 @@
2017-10-10 rocky <rb@dustyfeet.com>
2017-06-03 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 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>
* : 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/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-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: 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>
* : 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 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 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>
* 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>
* 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: Update
Table-driven info... Start a pysource unit test.
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>
* README.rst: Remove python versions tag I think it's messing up Pypi's very fussy formatting
2017-08-31 rocky <rb@dustyfeet.com>
* ChangeLog, NEWS, README.rst, __pkginfo__.py,
uncompyle6/parsers/parse37.py,
uncompyle6/semantics/make_function.py, uncompyle6/version.py: Get
ready for release 2.11.5
2017-08-15 rocky <rb@dustyfeet.com>
* Makefile: 3.7 support
2017-08-15 rocky <rb@dustyfeet.com>
* ChangeLog, NEWS, uncompyle6/version.py: Get ready for release
2.11.4
2017-08-15 rocky <rb@dustyfeet.com>
* __pkginfo__.py, pytest/validate.py, uncompyle6/parser.py,
uncompyle6/scanner.py: Misc cleanups... remove code now in xdis require at least xdis 3.5.4 PyPy tolerance
in validate testing
2017-08-13 rocky <rb@dustyfeet.com>
* pytest/test_basic.py, uncompyle6/parser.py, uncompyle6/scanner.py:
Allow 3-part version string lookups, e.g 2.7.1 We allow a float here, but if passed a string like '2.7'. or
'2.7.13', accept that in looking up either a scanner or a parser.
2017-08-10 rocky <rb@dustyfeet.com>
* README.rst: Link typo Name is trepan2 now not trepan
2017-08-09 rocky <rb@dustyfeet.com>
* ChangeLog, NEWS, README.rst, __pkginfo__.py,
uncompyle6/semantics/consts.py, uncompyle6/version.py: Get ready for
release 2.11.3 need xdis 3.5.1 for now. Adjust for xdis "is-not" which we need as
"is not"
2017-08-02 rocky <rb@dustyfeet.com>
* __pkginfo__.py: Revert commit to wrong branch
2017-08-02 rocky <rb@dustyfeet.com>
* __pkginfo__.py: Remove six from Python-2.4/2.5 package
2017-07-17 rocky <rb@dustyfeet.com>
* __pkginfo__.py, uncompyle6/scanners/scanner2.py,
uncompyle6/scanners/scanner3.py, uncompyle6/scanners/scanner30.py:
xdis's "exception match" is now "exception-match"
2017-07-15 rocky <rb@dustyfeet.com>
* __pkginfo__.py: xdis 3.5.1 is botched?
2017-07-14 rocky <rb@dustyfeet.com>
* __pkginfo__.py: Use newer xdis
2017-07-14 R. Bernstein <rocky@users.noreply.github.com>
* README.rst: Fixes issue #124
2017-07-14 rocky <rb@dustyfeet.com>
* HISTORY.md: History updates
2017-07-09 rocky <rb@dustyfeet.com>
* README.rst: RsT doc formatting
2017-07-09 rocky <rb@dustyfeet.com>
* ChangeLog, HOW-TO-REPORT-A-BUG.md, NEWS, uncompyle6/version.py:
Get ready for release 2.11.2
2017-07-08 rocky <rb@dustyfeet.com>
* __pkginfo__.py, uncompyle6/scanner.py,
uncompyle6/scanners/scanner2.py, uncompyle6/scanners/scanner26.py,
uncompyle6/scanners/scanner3.py, uncompyle6/scanners/scanner30.py,
uncompyle6/scanners/tok.py: Use xdis 3.5.0's opcode sets
2017-07-08 rocky <rb@dustyfeet.com>
* test/test_pyenvlib.py, uncompyle6/scanners/pypy32.py,
uncompyle6/scanners/pypy35.py, uncompyle6/scanners/scanner15.py,
uncompyle6/scanners/scanner32.py, uncompyle6/scanners/scanner34.py,
uncompyle6/scanners/scanner35.py, uncompyle6/scanners/scanner36.py:
Start supporting Pypy 3.5 (5.7.1-beta)
2017-07-05 rocky <rb@dustyfeet.com>
* test/simple_source/bug26/03_loop_if_cf.py,
uncompyle6/parsers/parse26.py: Loops in Python 2.4-2.6 loop
come_from Looks like Python 2.4-2.6 may have a COME_FROM(_LOOP) before the
jump_back. Fixes Issue #123
2017-06-29 rocky <rb@dustyfeet.com>
* : Work around not having real flow-control analysis
2017-06-28 rocky <rb@dustyfeet.com>
* uncompyle6/semantics/make_function.py: A guard against badly
formated bytecode
2017-06-25 rocky <rb@dustyfeet.com>
* ChangeLog, NEWS, test/simple_source/bug31/04_def_annotate.py,
uncompyle6/semantics/make_function.py,
uncompyle6/semantics/pysource.py: 3.x funciton and annotation bug
fixes
2017-06-25 rocky <rb@dustyfeet.com>
* uncompyle6/version.py: Get ready for release 2.11.1
2017-06-24 rocky <rb@dustyfeet.com>
* __pkginfo__.py, uncompyle6/scanner.py,
uncompyle6/scanners/scanner2.py, uncompyle6/scanners/scanner3.py,
uncompyle6/scanners/scanner30.py, uncompyle6/semantics/pysource.py:
Use xdis' instruction offset calculation fns.. next_offset, op_size, has_argument
2017-06-19 rocky <rb@dustyfeet.com>
* uncompyle6/semantics/pysource.py: Python 2 sometimes need
str->uncode in writing?
2017-06-19 rocky <rb@dustyfeet.com>
* uncompyle6/semantics/pysource.py: Allow deparsed out to be str as
well as unicode
2017-06-18 rocky <rb@dustyfeet.com>
* ChangeLog, NEWS, uncompyle6/version.py: Get ready for release
2.11.0
2017-06-13 rocky <rb@dustyfeet.com>
* uncompyle6/semantics/fragments.py: Adjust nodeInfo if it is a
Token
2017-06-13 rocky <rb@dustyfeet.com>
* uncompyle6/semantics/fragments.py: Add nonterminal node in
extractInfo
2017-06-10 rocky <rb@dustyfeet.com>
* uncompyle6/semantics/fragments.py,
uncompyle6/semantics/make_function.py: Fragment tag more expressions Revise make_function3 comment wrt args and kwargs
2017-06-10 rocky <rb@dustyfeet.com>
* uncompyle6/semantics/fragments.py: Fragment tag array subscripts
2017-06-10 R. Bernstein <rocky@users.noreply.github.com>
* README.rst: Create README.rst
2017-06-10 R. Bernstein <rocky@users.noreply.github.com>
* README.rst: Create README.rst
2017-06-10 rocky <rb@dustyfeet.com>
* uncompyle6/semantics/fragments.py: Set YIELD_VALUE offset in a
<yield> expr
2017-06-10 rocky <rb@dustyfeet.com>
* uncompyle6/semantics/make_function.py: Python 3.2 MAKE_FUNCTION
again.. Was handling bug32/01_named_and_kwargs.py wrong again
2017-06-09 R. Bernstein <rocky@users.noreply.github.com>
* : Merge pull request #119 from rocky/scan-longconstant Simplify access to L65536 ...
2017-06-09 rocky <rb@dustyfeet.com>
* uncompyle6/semantics/make_function.py: Attempt to document the
MAKE_FUNCTION/MAKE_LAMBDA mess... in Python 3.0+
2017-06-08 rocky <rb@dustyfeet.com>
* uncompyle6/semantics/make_function.py: Correct make_function3 for
Pytohn 3.2
2017-06-08 rocky <rb@dustyfeet.com>
* uncompyle6/semantics/pysource.py: Disable "continue" removal in
pysource.py "continue" could be the only statement and then removing it might
lead to a dangling "else".
2017-06-07 rocky <rb@dustyfeet.com>
* uncompyle6/semantics/fragments.py: Mark "pass" offsets. Start routine to find previous node.
2017-06-06 rocky <rb@dustyfeet.com>
* uncompyle6/parsers/parse3.py, uncompyle6/semantics/fragments.py:
Remove hacky fragments try fixup... hacky call_function code is also not needed or will be reinstated
properly. Better grammar structure for Python 3.6 call_function.
2017-06-05 rocky <rb@dustyfeet.com>
* uncompyle6/parsers/parse3.py, uncompyle6/parsers/parse36.py,
uncompyle6/scanners/scanner36.py: BUILD_{MAP,TUPLE}_UNPACK &
CALL_FUNCTION_EX_KW... Bang on these in 3.6. Not totally succesfull right now. In fact a
regression on one of the test cases
2017-06-05 rocky <rb@dustyfeet.com>
* uncompyle6/semantics/fragments.py: Important fragments bug fix... start, finish that had been adjusted wasn't getting reflected in
final returned deparsed.offsets dictionary. Redo keeping API
compatibility, i.e we still use namedtuple NodeInfo.
2017-06-04 rocky <rb@dustyfeet.com>
* uncompyle6/parsers/parse3.py, uncompyle6/semantics/pysource.py:
Python 3.5 *args with kwargs handling. 3.5 is a snowflake here. Thank you, Python. Fully fixes Issue 95. 3.6 is broken on this source, but for a *different* reason. Sigh.
* pytest/validate.py: 2.4 doesn't do six
2017-06-03 rocky <rb@dustyfeet.com>
* README.rst, __pkginfo__.py,
test/simple_source/bug35/04_CALL_FUNCTION_VAR_KW.py,
uncompyle6/semantics/fragments.py: Small changes. fragment tag EXEC_STMT
* appveyor.yml: Nope it (appveyor) doesn't.
2017-06-03 rocky <rb@dustyfeet.com>
* .travis.yml: Streamline .travis.yml a little bit
* __pkginfo__.py, appveyor.yml: Administrivia See if appveyor will handle 2.5
2017-06-03 rocky <rb@dustyfeet.com>
* : commit 7c299fbf3777c452d6a10075964961783f510699 Author: rocky
<rb@dustyfeet.com> Date: Sat Jun 3 05:38:05 2017 -0400
2017-06-03 rocky <rb@dustyfeet.com>
@@ -443,8 +40,12 @@
2017-05-30 rocky <rb@dustyfeet.com>
* ChangeLog, NEWS, uncompyle6/version.py: Get ready for release
2.10.0
* pytest/test_function_call.py: No decorators in Python < 2.6
2017-05-30 rocky <rb@dustyfeet.com>
* : commit ad98fae3d4b0b83f65b15da8201e33c0ee6dab17 Author: rocky
<rb@dustyfeet.com> Date: Tue May 30 01:26:52 2017 -0400
2017-05-30 rocky <rb@dustyfeet.com>
@@ -683,15 +284,25 @@
* ChangeLog, NEWS, uncompyle6/version.py: Get ready for release
2.9.11
2017-05-06 rocky <rb@dustyfeet.com>
* ChangeLog, NEWS, uncompyle6/version.py: Get ready for release
2.9.11
2017-05-06 rocky <rb@dustyfeet.com>
* test/Makefile: fix PYTHON variable setting in test/Makefile
2017-05-06 rocky <rb@dustyfeet.com>
* test/simple_source/bug32/01_try_except_raise.py,
test/simple_source/bug32/03_if.py, uncompyle6/parsers/parse32.py,
uncompyle6/parsers/parse33.py: Fix more Python3.2 parser errors
* test/Makefile, uncompyle6/scanners/scanner2.py,
uncompyle6/scanners/scanner3.py, uncompyle6/semantics/fragments.py:
Sync with master
2017-05-06 rocky <rb@dustyfeet.com>
* : commit 4a4782290490187ac2fcaaecd3ca808f933722b2 Author: rocky
<rb@dustyfeet.com> Date: Sat May 6 05:25:56 2017 -0400
2017-05-05 rocky <rb@dustyfeet.com>
@@ -879,6 +490,11 @@
haphazard way using real flow-control analysis. Hopefully that's on
the way. In the meantime we have this hack.
2017-04-14 rocky <rb@dustyfeet.com>
* : commit 7e8f7ba67431725fceec08344934c929a517efc5 Author: rocky
<rb@dustyfeet.com> Date: Fri Apr 14 05:42:44 2017 -0400
2017-04-14 rocky <rb@dustyfeet.com>
* test/simple_source/bug27+/03_if_1_else.py,
@@ -943,9 +559,10 @@
uncompyle6/parsers/parse2.py, uncompyle6/parsers/parse3.py,
uncompyle6/parsers/parse35.py: Add more while1else grammar rules Towards addressing issue #93
2017-04-09 rocky <rb@dustyfeet.com>
2017-04-10 rocky <rb@dustyfeet.com>
* : One more FUNCTION_VAR test for 3.3
* : commit b9703cf6b41138b717c282fc791c08d807692b07 Author: rocky
<rb@dustyfeet.com> Date: Sun Apr 9 06:58:41 2017 -0400
2017-04-09 rocky <rb@dustyfeet.com>
@@ -1056,11 +673,8 @@
2017-03-01 rocky <rb@dustyfeet.com>
* uncompyle6/scanner.py, uncompyle6/scanners/scanner2.py,
uncompyle6/scanners/scanner3.py, uncompyle6/verify.py: COME_FROM for
3.x POP_EXCEPT, DRY with op_name() ... Start adding COME_FROMs for POP_EXCEPT in preparation for getting
tryelse blocks correct. Simpler opname access functions: - self.op_name(op) is self.opc.opname[op] - self.op_name_from_offset(offset) is
self.opc.opname[self.code[offset]] verify.py: not all offsets are ints
* : commit 160ec0d9cc5fe347f6e8bdb69515a28c76cfb368 Author: rocky
<rb@dustyfeet.com> Date: Wed Mar 1 05:50:31 2017 -0500
2017-02-28 rocky <rb@dustyfeet.com>
@@ -1084,13 +698,17 @@
2017-02-25 rocky <rb@dustyfeet.com>
* ChangeLog, NEWS, __pkginfo__.py, uncompyle6/version.py: Get ready
for release 2.9.10
* : commit 1e3ea60055027dfd3f098661ac4f5979c5c48f7e Author: rocky
<rb@dustyfeet.com> Date: Sat Feb 25 20:18:03 2017 -0500
2017-02-25 rocky <rb@dustyfeet.com>
* uncompyle6/parser.py, uncompyle6/parsers/parse26.py: Python 2.6
parsing bugs .. and some parser list nonterminal cleanup
* uncompyle6/parser.py: Python <= 2.6 grammar fixes
2017-02-25 rocky <rb@dustyfeet.com>
* : commit 2fbbc728b10f0d3a754165708584bd80d33bc7f9 Author: rocky
<rb@dustyfeet.com> Date: Sat Feb 25 04:45:10 2017 -0500
2017-02-24 rocky <rb@dustyfeet.com>
@@ -1104,9 +722,15 @@
uncompyle6/parsers/parse25.py: Python 2.5 wasn't handling tryelse
properly
2017-02-20 rocky <rb@dustyfeet.com>
2017-02-22 rocky <rb@dustyfeet.com>
* : New test doesn't --verify correctly. Sigh.
* test/Makefile, test/simple_source/bug25/02_try_else.py,
uncompyle6/parsers/parse25.py: Python 2.5 was missing try else stmt
2017-02-22 rocky <rb@dustyfeet.com>
* : commit b043f6bafc9b9ae26e64dc0f1441d7abae894c37 Author: rocky
<rb@dustyfeet.com> Date: Mon Feb 20 09:22:01 2017 -0500
2017-02-20 rocky <rb@dustyfeet.com>
@@ -1145,6 +769,10 @@
* test/simple_source/bug22/01_ops.py, test/test_pythonlib.py: Beef
up grammar coverage
2017-02-10 rocky <rb@dustyfeet.com>
* test/Makefile: Group coverage Makefile targets
2017-01-29 rocky <rb@dustyfeet.com>
* test/Makefile, test/simple_source/bug22/01_ops.py,
@@ -1152,9 +780,24 @@
uncompyle6/semantics/pysource.py: Changes based on grammar coverage
info
2017-01-29 R. Bernstein <rocky@users.noreply.github.com>
2017-01-29 rocky <rb@dustyfeet.com>
* : Merge pull request #83 from rocky/coverage Coverage
* test/Makefile, test/simple_source/bug22/01_ops.py,
uncompyle6/parsers/parse25.py, uncompyle6/semantics/consts.py,
uncompyle6/semantics/pysource.py: Changes based on coverage
information
2017-01-29 rocky <rb@dustyfeet.com>
* : commit 9348411056cbe809e07c4ef341effa17bca90e2f Merge: 3dc766d
e71dd01 Author: R. Bernstein <rocky@users.noreply.github.com> Date:
Sun Jan 29 21:54:45 2017 -0500
2017-01-29 rocky <rb@dustyfeet.com>
* test/Makefile, test/simple_source/bug22/01_ops.py,
test/test_pyenvlib.py, test/test_pythonlib.py,
uncompyle6/semantics/consts.py: Simplfy getting coverage consts.py: notes on versions use which ops
2017-01-29 rocky <rb@dustyfeet.com>
@@ -1258,6 +901,10 @@
* uncompyle6/__init__.py: sys.recursionlimit is optional, not
essential
2017-01-11 rocky <rb@dustyfeet.com>
* NEWS, uncompyle6/version.py: Get ready for release 2.9.9
2017-01-11 rocky <rb@dustyfeet.com>
* : commit b131c20e99514d3a969a51e841d3a823017f1beb Author: rocky
@@ -1267,9 +914,22 @@
* ChangeLog, NEWS: Get ready for release 2.10.9
2017-01-11 rocky <rb@dustyfeet.com>
* uncompyle6/parsers/parse3.py, uncompyle6/scanner.py,
uncompyle6/semantics/make_function.py, uncompyle6/version.py: Merge
changes ... * str() in Python 2.4 doesn't detect unicode. * index() doesn't work on tuples * ifelse change
2017-01-11 rocky <rb@dustyfeet.com>
* : commit 7ece296f7638e71fad1117b940f7ffddbe095b1f Merge: 78a5b62
5035d54 Author: R. Bernstein <rocky@users.noreply.github.com> Date:
Wed Jan 11 07:10:23 2017 -0500
2017-01-11 R. Bernstein <rocky@users.noreply.github.com>
* : Merge pull request #79 from rocky/revert-78-patch-1 Revert "fix bug : not generate all files when use "-ro""
* uncompyle6/main.py: Revert "fix bug : not generate all files when
use "-ro""
2017-01-11 R. Bernstein <rocky@users.noreply.github.com>
@@ -1368,6 +1028,16 @@
* uncompyle6/parsers/parse35.py, uncompyle6/scanners/scanner3.py:
Python 3.5 continue detection bug
2017-01-02 rocky <rb@dustyfeet.com>
* uncompyle6/verify.py: 2.4 verify hacks
2017-01-02 rocky <rb@dustyfeet.com>
* : commit a7d93e88b4e0dfd6876a7a31bd201a0e40f24bea Merge: 9891494
136f42a Author: rocky <rb@dustyfeet.com> Date: Mon Jan 2 05:39:13
2017 -0500
2017-01-01 rocky <rb@dustyfeet.com>
* uncompyle6/scanners/scanner3.py: add come_from for setup_finally
@@ -1382,6 +1052,14 @@
* README.rst: Note how to verify correctness ... with --verify, --weak-verify and cross checking with pycdc
2016-12-31 rocky <rb@dustyfeet.com>
* uncompyle6/version.py: We are version 2.9.9
2016-12-31 rocky <rb@dustyfeet.com>
* uncompyle6/main.py: 2.7->2.4 conversion
2016-12-31 rocky <rb@dustyfeet.com>
* ChangeLog, NEWS, uncompyle6/version.py: Get ready for release
@@ -1391,6 +1069,13 @@
* uncompyle6/parsers/parse26.py: 2.x list_if may have a THEN in it
2016-12-31 rocky <rb@dustyfeet.com>
* test/Makefile, uncompyle6/main.py, uncompyle6/parsers/parse26.py,
uncompyle6/scanners/scanner2.py, uncompyle6/scanners/scanner26.py,
uncompyle6/scanners/scanner3.py, uncompyle6/semantics/pysource.py,
uncompyle6/verify.py: Merge master branche Handle 2.2 list_if
2016-12-31 rocky <rb@dustyfeet.com>
* uncompyle6/scanners/scanner3.py: Towards fixing a Python 3.3
@@ -1419,10 +1104,9 @@
* : Merge pull request #73 from rocky/then-crap Add THEN token to improve Python 2.2-2.6 control flow detection
2016-12-28 rocky <rb@dustyfeet.com>
2016-12-29 R. Bernstein <rocky@users.noreply.github.com>
* uncompyle6/parsers/parse3.py, uncompyle6/scanners/tok.py: Misc
bugs
* : Merge pull request #72 from rocky/master THEN psuedo-ops for Python 2.x
2016-12-28 rocky <rb@dustyfeet.com>
@@ -1449,8 +1133,7 @@
2016-12-27 rocky <rb@dustyfeet.com>
* uncompyle6/scanners/scanner2.py: Make 2.6 and 2.7 ingest more
alike
* : Merge commit '9b1dd0f' into python-2.4
2016-12-26 rocky <rb@dustyfeet.com>
@@ -1470,6 +1153,11 @@
* uncompyle6/parsers/parse25.py: fix bug in using python2 AST rules
in python 2.5
2016-12-26 rocky <rb@dustyfeet.com>
* uncompyle6/parsers/parse25.py: Bug in using python2 ast checking
in python 2.5
2016-12-26 rocky <rb@dustyfeet.com>
* : commit f1a947f106b231fb1480ba301b15e3ceaf78c94f Author: rocky
@@ -1482,15 +1170,19 @@
uncompyle6/verify.py: Scanner call fixes. NAME_MODULE removal for
<=2.4
2016-12-24 rocky <rb@dustyfeet.com>
2016-12-25 rocky <rb@dustyfeet.com>
* uncompyle6/parsers/astnode.py, uncompyle6/parsers/parse2.py,
uncompyle6/parsers/parse26.py, uncompyle6/parsers/parse3.py,
uncompyle6/parsers/parse36.py, uncompyle6/scanners/scanner15.py,
uncompyle6/scanners/scanner2.py, uncompyle6/scanners/scanner21.py,
uncompyle6/scanners/scanner22.py,
uncompyle6/semantics/fragments.py, uncompyle6/semantics/pysource.py:
Lint
* uncompyle6/main.py, uncompyle6/scanners/scanner15.py,
uncompyle6/scanners/scanner21.py, uncompyle6/scanners/scanner22.py,
uncompyle6/scanners/scanner23.py, uncompyle6/scanners/scanner24.py,
uncompyle6/semantics/pysource.py, uncompyle6/verify.py: Removing
NAME_MODULE, lint and bug fixes scanner*.py: show_asm param is optional verify.py: call correct
scanners main.py, verify.py: Use older Python print statements
2016-12-25 rocky <rb@dustyfeet.com>
* : commit e3f4beeb74e33d5b404094765cc63040f62a0b41 Author: rocky
<rb@dustyfeet.com> Date: Sat Dec 24 07:45:02 2016 -0500
2016-12-24 rocky <rb@dustyfeet.com>
@@ -1511,22 +1203,34 @@
2016-12-18 rocky <rb@dustyfeet.com>
* pytest/.gitignore, test/simple_source/bug25/02_try_else.py,
uncompyle6/parsers/parse25.py, uncompyle6/parsers/parse26.py: Python
2.5 mistaken try/else
* : commit c7c0a989829a9a625333665516387c1177c611c2 Author: rocky
<rb@dustyfeet.com> Date: Sun Dec 18 00:56:07 2016 -0500
2016-12-17 rocky <rb@dustyfeet.com>
* uncompyle6/scanners/scanner2.py, uncompyle6/scanners/scanner25.py:
show-asm on python2.5 is optional make scanner2 look a little more like scanner3
2016-12-17 rocky <rb@dustyfeet.com>
* uncompyle6/scanners/scanner2.py, uncompyle6/scanners/scanner25.py:
show-asm on python2.5 is optional Make scanner2 a little more like scanner3.
2016-12-17 rocky <rb@dustyfeet.com>
* uncompyle6/parser.py, uncompyle6/scanner.py,
uncompyle6/scanners/scanner2.py, uncompyle6/scanners/scanner3.py,
uncompyle6/semantics/fragments.py: Python 2.6/2.7 tolerance in
Python 2.4 branch
2016-12-16 rocky <rb@dustyfeet.com>
* NEWS: Release 2.9.8 news
2016-12-16 rocky <rb@dustyfeet.com>
* __pkginfo__.py, uncompyle6/version.py: Get ready for release 2.9.8
* : commit 13d5cd1a588b7f4f2c233c436ce6b0b39db9950e Author: rocky
<rb@dustyfeet.com> Date: Fri Dec 16 22:42:46 2016 -0500
2016-12-16 rocky <rb@dustyfeet.com>
@@ -1594,15 +1298,12 @@
2016-12-04 rocky <rb@dustyfeet.com>
* ChangeLog, NEWS, uncompyle6/main.py, uncompyle6/parser.py,
uncompyle6/parsers/parse26.py, uncompyle6/parsers/parse3.py,
uncompyle6/parsers/parse34.py, uncompyle6/parsers/parse35.py,
uncompyle6/scanner.py, uncompyle6/scanners/scanner2.py,
uncompyle6/scanners/scanner23.py, uncompyle6/scanners/scanner24.py,
uncompyle6/scanners/scanner3.py, uncompyle6/scanners/tok.py,
uncompyle6/semantics/make_function.py,
uncompyle6/semantics/pysource.py, uncompyle6/verify.py,
uncompyle6/version.py: Get ready for release 2.9.7 Some of the many lint things. Linting is kind of stupid though.
* ChangeLog: Get ready for release 2.9.7
2016-12-04 rocky <rb@dustyfeet.com>
* : commit d22931cb49f0e28a0fbe48a7c1526b1f170a5b52 Author: rocky
<rb@dustyfeet.com> Date: Sun Dec 4 07:31:34 2016 -0500
2016-11-28 rocky <rb@dustyfeet.com>
@@ -1653,11 +1354,34 @@
* uncompyle6/semantics/pysource.py: Better line number tracking Indent Python 2 list comprehensions, albeit badly. DRY code a
little via indent_if_source_nl
2016-11-24 rocky <rb@dustyfeet.com>
* circle.yml: CircleCI build
2016-11-24 rocky <rb@dustyfeet.com>
* uncompyle6/parsers/parse3.py: Remove dup Python 3 grammar rule
2016-11-24 rocky <rb@dustyfeet.com>
* uncompyle6/parsers/parse3.py, uncompyle6/scanners/scanner2.py:
<2.7 "if" detection and dup Python 3 grammar rule
2016-11-24 rocky <rb@dustyfeet.com>
* test/test_pyenvlib.py, uncompyle6/linenumbers.py,
uncompyle6/main.py, uncompyle6/scanners/scanner2.py,
uncompyle6/semantics/make_function.py,
uncompyle6/semantics/pysource.py, uncompyle6/verify.py: Bug in 2.4
"if" dectection and... Wrong language used in old-style exceptions: use "except Error,e"
not "except Error(e)""
2016-11-24 rocky <rb@dustyfeet.com>
* __pkginfo__.py, pytest/test_grammar.py, uncompyle6/parser.py,
uncompyle6/parsers/parse26.py: Python 2.6 grammary bug and.. __pkginfo.py__: Bump spark_parser
version for parse_flags 'dups'
2016-11-23 rocky <rb@dustyfeet.com>
* __pkginfo__.py, pytest/test_grammar.py, uncompyle6/parser.py,
@@ -1669,8 +1393,17 @@
2016-11-23 rocky <rb@dustyfeet.com>
* : commit 6aa1531972de83ecab15b4c96b89c873ea5a7458 Author: rocky
<rb@dustyfeet.com> Date: Wed Nov 23 00:48:38 2016 -0500
* : commit 7133540c235e16f02d2db62cb903b70aa311de20 Author: rocky
<rb@dustyfeet.com> Date: Wed Nov 23 08:26:12 2016 -0500
2016-11-23 rocky <rb@dustyfeet.com>
* : commit a9349b8f3d12b2aa0cd88286617c1af9cccad018 Author: rocky
<rb@dustyfeet.com> Date: Tue Nov 22 17:49:47 2016 -0500
2016-11-23 rocky <rb@dustyfeet.com>
* circle.yml: Circle CI uses 2.7.10 and 2.7.12 is not available
2016-11-22 rocky <rb@dustyfeet.com>

View File

@@ -44,8 +44,8 @@ it appears that Hartmut did most of the work to get this code to
accept the full Python language. He added precedence to the table
specifiers, support for multiple versions of Python, the
pretty-printing of docstrings, lists, and hashes. He also wrote test and verification routines of
deparsed bytecode, and used this in an extensive set of tests that he also wrote. He says he could verify against the
entire Python library. However I have subsequently found small and relatively obscure bugs in the decompilation code.
deparsed bytecode, and used this in an extensive set of tests that he also wrote. He says he could verify against the
entire Python library. However I have subsequently found small and relatively obscure bugs in the decompilation code.
decompyle2.2 was packaged for Debian (sarge) by
[Ben Burton around 2002](https://packages.qa.debian.org/d/decompyle.html). As
@@ -66,7 +66,7 @@ code to handle first Python 2.3 and then 2.4 bytecodes. Because of
jump optimization introduced in the CPython bytecode compiler at that
time, various JUMP instructions were classifed as going backwards, and
COME FROM instructions were reintroduced. See
[RELEASE-2.4-CHANGELOG.txt](https://github.com/rocky/python-uncompyle6/blob/master/DECOMPYLE-2.4-CHANGELOG.txt)
[RELEASE-2.4-CHANGELOG.txt](https://github.com/rocky/python-uncompyle6/blob/master/DECOMPYLE-2.4-CHANGELOG.txt)
for more details here. There wasn't a public
release of RELEASE-2.4 and bytecodes other than Python 2.4 weren't
supported. Dan says the Python 2.3 version could verify the entire
@@ -99,7 +99,7 @@ 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
transforming the byte code into a a pseudo 2.7 python bytecode and is
based on code from Eloi Vanderbeken.
This project, `uncompyle6`, abandons that approach for various
@@ -120,10 +120,10 @@ while, handling Python bytecodes from Python versions 2.5+ and
3.2+. In doing so, it has been expedient to separate this into three
projects:
* marshaling/unmarshaling, bytecode loading and disassembly ([xdis](https://pypi.python.org/pypi/xdis)),
* bytecode loading and disassembly ([xdis](https://pypi.python.org/pypi/xdis)),
* parsing and tree building ([spark_parser](https://pypi.python.org/pypi/spark_parser)),
* this project - grammar and semantic actions for decompiling
([uncompyle6](https://pypi.python.org/pypi/uncompyle6)).
([uncompyle6](https://pypi.python.org/pypi/spark_parser)).
Over the many years, code styles and Python features have
@@ -162,8 +162,5 @@ support has been lagging.
Tests for the project have been, or are being, culled from all of the
projects mentioned.
For a little bit of the history of changes to the Early-algorithm parser,
see the file [NEW-FEATURES.rst](https://github.com/rocky/python-spark/blob/master/NEW-FEATURES.rst) in the [python-spark github repository](https://github.com/rocky/python-spark).
NB. If you find mistakes, want corrections, or want your name added
(or removed), please contact me.

View File

@@ -3,9 +3,8 @@
## 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.
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.
I have found bugs in *every* Python decompiler I have tried. Even
those where authors/maintainers claim that they have used it on
@@ -15,67 +14,12 @@ but that the program is *semantically* not equivalent.
So it is likely you'll find a mistranslation in decompiling.
## Is it really a bug?
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)
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. If you can't attach it to the issue, or create a github gist,
then the code you are sending is too large.
Please also try to narrow the bug. See below.
* Source text
## What to send (additional helpful information)
@@ -106,7 +50,7 @@ one fool can learn, so can another."
## Narrowing the problem
I don't need or want the entire source code base for which one file or module
I don't need 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.

View File

@@ -36,10 +36,8 @@ check-2.7 check-3.3 check-3.4: pytest
check-3.0 check-3.1 check-3.2 check-3.5 check-3.6:
$(MAKE) -C test $@
check-3.7: pytest
#:Tests for Python 2.6 (doesn't have pytest)
check-2.6:
check-2.4 check-2.5 check-2.6:
$(MAKE) -C test $@
#:PyPy 2.6.1 or PyPy 5.0.1
@@ -61,7 +59,7 @@ clean: clean_pyc
#: Create source (tarball) and wheel distribution
dist:
$(PYTHON) ./setup.py sdist bdist_wheel
$(PYTHON) ./setup.py sdist bdist_egg
#: Remove .pyc files
clean_pyc:
@@ -89,7 +87,7 @@ bdist_egg:
#: Create binary wheel distribution
wheel:
bdist_wheel:
$(PYTHON) ./setup.py bdist_wheel

93
NEWS
View File

@@ -1,61 +1,4 @@
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,
e.g. 2.7.1 We allow a float here, but if passed a string like '2.7'. or
* unpin 3.5.1. xdis 3.5.4 has been releasd and fixes the problems we had. Use that.
* some routnes here moved to xdis. Use the xdis version
* README.rst: Link typo Name is trepan2 now not trepan
* xdis-forced change adjust for COMPARE_OP "is-not" in
semanatic routines. We need "is not".
* Some PyPy tolerance in validate testing.
* Some pyston tolerance
uncompyle6 2.11.3 2017-08-09
Very minor changes
- RsT doc fixes and updates
- use newer xdis, but not too new; 3.5.2 breaks uncompyle6
- use xdis opcode sets
- xdis "exception match" is now "exception-match"
uncompyle6 2.11.2 2017-07-09
- Start supporting Pypy 3.5 (5.7.1-beta)
- use xdis 3.5.0's opcode sets and require xdis 3.5.0
- Correct some Python 2.4-2.6 loop detection
- guard against badly formatted bytecode
uncompyle6 2.11.1 2017-06-25
- Python 3.x annotation and function signature fixes
- Bump xdis version
- Small pysource bug fixes
uncompyle6 2.11.0 2017-06-18 Fleetwood
uncompyle6 2.11.1 2016-06-18 Fleetwood
- Major improvements in fragment tracking
* Add nonterminal node in extractInfo
* tag more offsets in expressions
@@ -65,17 +8,17 @@ uncompyle6 2.11.0 2017-06-18 Fleetwood
- Fixes yet again for make_function node handling; document what's up here
- Fix bug in snowflake Python 3.5 *args kwargs
uncompyle6 2.10.1 2017-06-3 Marylin Frankel
uncompyle6 2.10.1 2016-06-3 Marylin Frankel
- fix some fragments parsing bugs
- was returning the wrong type sometimes in deparse_code_around_offset()
- capture function name in offsets
- track changes to ifelstrmtr node from pysource into fragments
uncompyle6 2.10.0 2017-05-30 Elaine Gordon
uncompyle6 2.10.0 2016-05-30 Elaine Gordon
- Add fuzzy offset deparse look up
- 3.6 bug fixes
- Add fuzzy offset deparse lookup
- 3.6 bugfixes
- fix EXTENDED_ARGS handling (and in 2.6 and others)
- semantic routine make_function fragments.py
- MAKE_FUNCTION handling
@@ -86,19 +29,19 @@ uncompyle6 2.10.0 2017-05-30 Elaine Gordon
- 3.5 FUNCTION_VAR bug
- 3.x pass statement insdie while True
- Improve 3.2 decompilation
- Fixed -o argument processing (grkov90)
- Fixed -o argument processing (Gregrory)
- Reduce scope of LOAD_ASSERT as expr to 3.4+
- "await" statement fixes
- 2.3, 2.4 "if 1 .." fixes
- 3.x annotation fixes
uncompyle6 2.9.11 2017-04-06
uncompyle6 2.9.11 2016-04-06
- Better support for Python 3.5+ BUILD_MAP_UNPACK
- Start 3.6 CALL_FUNCTION_EX support
- Many decompilation bug fixes. (Many more remain). See ChangeLog
uncompyle6 2.9.10 2017-02-25
uncompyle6 2.9.10 2016-02-25
- Python grammar rule fixes
- Add ability to get grammar coverage on runs
@@ -165,7 +108,7 @@ uncompyle6 2.9.6 2016-11-20
uncompyle6 2.9.5 2016-11-13
- Fix Python 3 bugs:
* improper while 1 else
* improprer while 1 else
* docstring indent
* 3.3 default values in lambda expressions
* start 3.0 decompilation (needs newer xdis)
@@ -175,12 +118,12 @@ uncompyle6 2.9.5 2016-11-13
uncompyle6 2.9.4 2016-11-02
- Handle Python 3.x function annotations
- track def keyword-parameter line-splitting in source code better
- track def keywoard-parameter line-splitting in source code better
- bump min xdis version to mask previous xdis bug
uncompyle6 2.9.3 2016-10-26
Release forced by incompatibility change in xdis 3.2.0.
Release forced by incompatiblity change in xdis 3.2.0.
- Python 3.1 bugs:
* handle "with ... as"
@@ -212,7 +155,7 @@ uncompyle6 2.9.0 2016-10-09
this Forces change in requirements.txt and _pkg_info_.py
- Start Python 1.5 decompiling; another round of work is needed to
remove bugs
- Simplify python 2.1 grammar
- Simpify python 2.1 grammar
- Fix bug with -t ... Wasn't showing source text when -t option was given
- Fix 2.1-2.6 bug in list comprehension
@@ -235,7 +178,7 @@ control-flow structure detection is done.
. 3.0 .. 3.2 *args processing
. 3.0 .. 3.2 call name and kwargs bug
. 3.0 .. getting parameter of *
. 3.0 .. handling variable number of args
. 3.0 .. handling varible number of args
. 3.0 .. "if" structure bugs
* 3.5+ if/else bugs
* 2.2-2.6 bugs
@@ -286,7 +229,7 @@ uncompyle6 2.7.1 2016-07-26
uncompyle6 2.7.0 2016-07-15
- Many Syntax and verification bugs removed
- Many Syntax and verifification bugs removed
tested on standard libraries from 2.3.7 to 3.5.1
and they all decompile and verify fine.
I'm sure there are more bugs though.
@@ -313,9 +256,9 @@ uncompyle6 2.6.0 2016-07-07
- Better <2.6 vs. 2.7 grammar separation
- Fix some 2.7 deparsing bugs
- Fix bug in installing uncompyle6 script
- Doc improvements
- Doc improvments
uncompyle6 2.5.0 2016-06-22 Summer Solstice
uncompyle6 2.5.0 2016-06-22 Summer Solstace
- Much better Python 3.2-3.5 coverage.
3.4.6 is probably the best;3.2 and 3.5 are weaker
@@ -327,7 +270,7 @@ uncompyle6 2.5.0 2016-06-22 Summer Solstice
uncompyle6 2.4.0 2016-05-18 (in memory of Lewis Bernstein)
- Many Python 3 bugs fixed:
* Python 3.2 to 3.5 libraries largely
* Python 3.2 to 3.5 libaries largely
uncompyle and most verify
- pydisassembler:
* disassembles all code objects in a file
@@ -385,7 +328,7 @@ uncompyle6 2.2.0 2016-04-30
uncompyle6 2.2.0 2016-04-02
- Support single-mode (in addition to exec-mode) compilation
- Support single-mode (in addtion to exec-mode) compilation
- Start to DRY Python 2 and Python 3 grammars
- Fix bug in if else ternary construct
- Fix bug in uncomplye6 -d and -r options (via lelicopter)

View File

@@ -1,10 +1,10 @@
|buildstatus|
|buildstatus| |Supported Python Versions|
uncompyle6
==========
A native Python cross-version Decompiler and Fragment Decompiler.
The successor to decompyle, uncompyle, and uncompyle2.
Follows in the tradition of decompyle, uncompyle, and uncompyle2.
Introduction
@@ -12,7 +12,7 @@ Introduction
*uncompyle6* translates Python bytecode back into equivalent Python
source code. It accepts bytecodes from Python version 1.5, and 2.1 to
3.7 or so, including PyPy bytecode and Dropbox's Python 2.5 bytecode.
3.6 or so, including PyPy bytecode and Dropbox's Python 2.5 bytecode.
Why this?
---------
@@ -171,12 +171,9 @@ See Also
* https://code.google.com/archive/p/unpyc3/ : supports Python 3.2 only. The above projects use a different decompiling technique than what is used here.
* https://github.com/figment/unpyc3/ : fork of above, but supports Python 3.3 only. Include some fixes like supporting function annotations
* The HISTORY_ file.
* `How to report a bug <https://github.com/rocky/python-uncompyle6/blob/master/HOW-TO-REPORT-A-BUG.md>`_
* https://github.com/rocky/python-xdis : Cross Python version disassembler
* https://github.com/rocky/python-xasm : Cross Python version assembler
.. _trepan: https://pypi.python.org/pypi/trepan2
* `How to report a bug <https://github.com/rocky/python-uncompyle6/blob/master/HISTORY.md>`_
.. |downloads| image:: https://img.shields.io/pypi/dd/uncompyle6.svg
.. _trepan: https://pypi.python.org/pypi/trepan
.. _HISTORY: https://github.com/rocky/python-uncompyle6/blob/master/HISTORY.md
.. _debuggers: https://pypi.python.org/pypi/trepan3k
.. _remake: https://bashdb.sf.net/remake
@@ -184,5 +181,7 @@ See Also
.. _this: https://github.com/rocky/python-uncompyle6/wiki/Deparsing-technology-and-its-use-in-exact-location-reporting
.. |buildstatus| image:: https://travis-ci.org/rocky/python-uncompyle6.svg
:target: https://travis-ci.org/rocky/python-uncompyle6
.. |Supported Python Versions| image:: https://img.shields.io/pypi/pyversions/uncompyle6.svg
:target: https://pypi.python.org/pypi/uncompyle6/
.. _PJOrion: http://www.koreanrandom.com/forum/topic/15280-pjorion-%D1%80%D0%B5%D0%B4%D0%B0%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%86%D0%B8%D1%8F-%D0%B4%D0%B5%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%86%D0%B8%D1%8F-%D0%BE%D0%B1%D1%84
.. _Deobfuscator: https://github.com/extremecoders-re/PjOrion-Deobfuscator

View File

@@ -39,8 +39,8 @@ entry_points = {
'pydisassemble=uncompyle6.bin.pydisassemble:main',
]}
ftp_url = None
install_requires = ['spark-parser >= 1.7.0, < 1.8.0',
'xdis >= 3.6.0, < 3.7.0', 'six']
install_requires = ['spark-parser >= 1.6.1, < 1.7.0',
'xdis >= 3.3.1, < 3.4.0']
license = 'MIT'
mailing_list = 'python-debugger@googlegroups.com'
modname = 'uncompyle6'

View File

@@ -10,4 +10,4 @@ dependencies:
- pip install -r requirements-dev.txt
test:
override:
- python ./setup.py develop && make check-2.7
- python ./setup.py develop && make check-2.6

View File

@@ -1,11 +0,0 @@
from uncompyle6.scanner import get_scanner
from uncompyle6.parser import get_python_parser
def test_get_scanner():
# See that we can retrieve a scanner using a full version number
assert get_scanner('2.7.13')
def test_get_parser():
# See that we can retrieve a sparser using a full version number
assert get_python_parser('2.7.13')

View File

@@ -1,150 +0,0 @@
# std
import os
# test
import pytest
import hypothesis
from hypothesis import strategies as st
# uncompyle6
from uncompyle6 import PYTHON_VERSION, deparse_code
@st.composite
def expressions(draw):
# todo : would be nice to generate expressions using hypothesis however
# this is pretty involved so for now just use a corpus of expressions
# from which to select.
return draw(st.sampled_from((
'abc',
'len(items)',
'x + 1',
'lineno',
'container',
'self.attribute',
'self.method()',
# These expressions are failing, I think these are control
# flow problems rather than problems with FORMAT_VALUE,
# however I need to confirm this...
#'sorted(items, key=lambda x: x.name)',
#'func(*args, **kwargs)',
#'text or default',
#'43 if life_the_universe and everything else None'
)))
@st.composite
def format_specifiers(draw):
"""
Generate a valid format specifier using the rules:
format_spec ::= [[fill]align][sign][#][0][width][,][.precision][type]
fill ::= <any character>
align ::= "<" | ">" | "=" | "^"
sign ::= "+" | "-" | " "
width ::= integer
precision ::= integer
type ::= "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%"
See https://docs.python.org/2/library/string.html
:param draw: Let hypothesis draw from other strategies.
:return: An example format_specifier.
"""
alphabet_strategy = st.characters(min_codepoint=ord('a'), max_codepoint=ord('z'))
fill = draw(st.one_of(alphabet_strategy, st.none()))
align = draw(st.sampled_from(list('<>=^')))
fill_align = (fill + align or '') if fill else ''
type_ = draw(st.sampled_from('bcdeEfFgGnosxX%'))
can_have_sign = type_ in 'deEfFgGnoxX%'
can_have_comma = type_ in 'deEfFgG%'
can_have_precision = type_ in 'fFgG'
can_have_pound = type_ in 'boxX%'
can_have_zero = type_ in 'oxX'
sign = draw(st.sampled_from(list('+- ') + [''])) if can_have_sign else ''
pound = draw(st.sampled_from(('#', '',))) if can_have_pound else ''
zero = draw(st.sampled_from(('0', '',))) if can_have_zero else ''
int_strategy = st.integers(min_value=1, max_value=1000)
width = draw(st.one_of(int_strategy, st.none()))
width = str(width) if width is not None else ''
comma = draw(st.sampled_from((',', '',))) if can_have_comma else ''
if can_have_precision:
precision = draw(st.one_of(int_strategy, st.none()))
precision = '.' + str(precision) if precision else ''
else:
precision = ''
return ''.join((fill_align, sign, pound, zero, width, comma, precision, type_,))
@st.composite
def fstrings(draw):
"""
Generate a valid f-string.
See https://www.python.org/dev/peps/pep-0498/#specification
:param draw: Let hypothsis draw from other strategies.
:return: A valid f-string.
"""
character_strategy = st.characters(
blacklist_characters='\r\n\'\\s{}',
min_codepoint=1,
max_codepoint=1000,
)
is_raw = draw(st.booleans())
integer_strategy = st.integers(min_value=0, max_value=3)
expression_count = draw(integer_strategy)
content = []
for _ in range(expression_count):
expression = draw(expressions())
conversion = draw(st.sampled_from(('', '!s', '!r', '!a',)))
has_specifier = draw(st.booleans())
specifier = ':' + draw(format_specifiers()) if has_specifier else ''
content.append('{{{}{}}}'.format(expression, conversion, specifier))
content.append(draw(st.text(character_strategy)))
content = ''.join(content)
return "f{}'{}'".format('r' if is_raw else '', content)
@pytest.mark.skipif(PYTHON_VERSION < 3.6, reason='need at least python 3.6')
@hypothesis.given(format_specifiers())
def test_format_specifiers(format_specifier):
"""Verify that format_specifiers generates valid specifiers"""
try:
exec('"{:' + format_specifier + '}".format(0)')
except ValueError as e:
if 'Unknown format code' not in str(e):
raise
def run_test(text):
hypothesis.assume(len(text))
hypothesis.assume("f'{" in text)
expr = text + '\n'
code = compile(expr, '<string>', 'single')
deparsed = deparse_code(PYTHON_VERSION, code, compile_mode='single')
recompiled = compile(deparsed.text, '<string>', 'single')
if recompiled != code:
assert 'dis(' + deparsed.text.strip('\n') + ')' == 'dis(' + expr.strip('\n') + ')'
@pytest.mark.skipif(PYTHON_VERSION < 3.6, reason='need at least python 3.6')
@hypothesis.given(fstrings())
def test_uncompyle_fstring(fstring):
"""Verify uncompyling fstring bytecode"""
run_test(fstring)
@pytest.mark.skipif(PYTHON_VERSION < 3.6, reason='need at least python 3.6')
@pytest.mark.parametrize('fstring', [
"f'{abc}{abc!s}'",
"f'{abc}0'",
])
def test_uncompyle_direct(fstring):
"""useful for debugging"""
run_test(fstring)

View File

@@ -1,175 +0,0 @@
# std
import string
# 3rd party
from hypothesis import given, assume, example, settings, strategies as st
import pytest
# uncompyle
from validate import validate_uncompyle
from test_fstring import expressions
alpha = st.sampled_from(string.ascii_lowercase)
numbers = st.sampled_from(string.digits)
alphanum = st.sampled_from(string.ascii_lowercase + string.digits)
@st.composite
def function_calls(draw,
min_keyword_args=0, max_keyword_args=5,
min_positional_args=0, max_positional_args=5,
min_star_args=0, max_star_args=1,
min_double_star_args=0, max_double_star_args=1):
"""
Strategy factory for generating function calls.
:param draw: Callable which draws examples from other strategies.
:return: The function call text.
"""
st_positional_args = st.lists(
alpha,
min_size=min_positional_args,
max_size=max_positional_args
)
st_keyword_args = st.lists(
alpha,
min_size=min_keyword_args,
max_size=max_keyword_args
)
st_star_args = st.lists(
alpha,
min_size=min_star_args,
max_size=max_star_args
)
st_double_star_args = st.lists(
alpha,
min_size=min_double_star_args,
max_size=max_double_star_args
)
positional_args = draw(st_positional_args)
keyword_args = draw(st_keyword_args)
st_values = st.lists(
expressions(),
min_size=len(keyword_args),
max_size=len(keyword_args)
)
keyword_args = [
x + '=' + e
for x, e in
zip(keyword_args, draw(st_values))
]
star_args = ['*' + x for x in draw(st_star_args)]
double_star_args = ['**' + x for x in draw(st_double_star_args)]
arguments = positional_args + keyword_args + star_args + double_star_args
draw(st.randoms()).shuffle(arguments)
arguments = ','.join(arguments)
function_call = 'fn({arguments})'.format(arguments=arguments)
try:
# TODO: Figure out the exact rules for ordering of positional, keyword,
# star args, double star args and in which versions the various
# types of arguments are supported so we don't need to check that the
# expression compiles like this.
compile(function_call, '<string>', 'single')
except:
assume(False)
return function_call
def test_function_no_args():
validate_uncompyle("fn()")
def isolated_function_calls(which):
"""
Returns a strategy for generating function calls, but isolated to
particular types of arguments, for example only positional arguments.
This can help reason about debugging errors in specific types of function
calls.
:param which: One of 'keyword', 'positional', 'star', 'double_star'
:return: Strategy for generating an function call isolated to specific
argument types.
"""
kwargs = dict(
max_keyword_args=0,
max_positional_args=0,
max_star_args=0,
max_double_star_args=0,
)
kwargs['_'.join(('min', which, 'args'))] = 1
kwargs['_'.join(('max', which, 'args'))] = 5 if 'star' not in which else 1
return function_calls(**kwargs)
with settings(max_examples=25):
@given(isolated_function_calls('positional'))
@example("fn(0)")
def test_function_positional_only(expr):
validate_uncompyle(expr)
@given(isolated_function_calls('keyword'))
@example("fn(a=0)")
def test_function_call_keyword_only(expr):
validate_uncompyle(expr)
@given(isolated_function_calls('star'))
@example("fn(*items)")
def test_function_call_star_only(expr):
validate_uncompyle(expr)
@given(isolated_function_calls('double_star'))
@example("fn(**{})")
def test_function_call_double_star_only(expr):
validate_uncompyle(expr)
@pytest.mark.xfail()
def test_BUILD_CONST_KEY_MAP_BUILD_MAP_UNPACK_WITH_CALL_BUILD_TUPLE_CALL_FUNCTION_EX():
validate_uncompyle("fn(w=0,m=0,**v)")
@pytest.mark.xfail()
def test_BUILD_MAP_BUILD_MAP_UNPACK_WITH_CALL_BUILD_TUPLE_CALL_FUNCTION_EX():
validate_uncompyle("fn(a=0,**g)")
@pytest.mark.xfail()
def test_CALL_FUNCTION_EX():
validate_uncompyle("fn(*g,**j)")
@pytest.mark.xfail()
def test_BUILD_MAP_CALL_FUNCTION_EX():
validate_uncompyle("fn(*z,u=0)")
@pytest.mark.xfail()
def test_BUILD_TUPLE_CALL_FUNCTION_EX():
validate_uncompyle("fn(**a)")
@pytest.mark.xfail()
def test_BUILD_MAP_BUILD_TUPLE_BUILD_TUPLE_UNPACK_WITH_CALL_CALL_FUNCTION_EX():
validate_uncompyle("fn(b,b,b=0,*a)")
@pytest.mark.xfail()
def test_BUILD_TUPLE_BUILD_TUPLE_UNPACK_WITH_CALL_CALL_FUNCTION_EX():
validate_uncompyle("fn(*c,v)")
@pytest.mark.xfail()
def test_BUILD_CONST_KEY_MAP_CALL_FUNCTION_EX():
validate_uncompyle("fn(i=0,y=0,*p)")
@pytest.mark.skip(reason='skipping property based test until all individual tests are passing')
@given(function_calls())
def test_function_call(function_call):
validate_uncompyle(function_call)

View File

@@ -11,16 +11,15 @@ 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.dump_grammar())
"Remaining tokens %s\n====\n%s" % (remain_tokens, p.dumpGrammar())
p = get_python_parser(PYTHON_VERSION, is_pypy=IS_PYPY)
lhs, rhs, tokens, right_recursive = p.check_sets()
lhs, rhs, tokens, right_recursive = p.checkSets()
expect_lhs = set(['expr1024', 'pos_arg'])
unused_rhs = set(['build_list', 'call_function', 'mkfunc',
'mklambda',
'unpack', 'unpack_list'])
expect_right_recursive = frozenset([('designList',
('designator', 'DUP_TOP', 'designList'))])
expect_right_recursive = [['designList', ('designator', 'DUP_TOP', 'designList')]]
if PYTHON3:
expect_lhs.add('load_genexpr')
@@ -40,14 +39,13 @@ def test_grammar():
s = get_scanner(PYTHON_VERSION, IS_PYPY)
ignore_set = set(
"""
JUMP_BACK CONTINUE
JUMP_BACK CONTINUE RETURN_END_IF
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_END_IF RETURN_END_IF_LAMBDA RETURN_VALUE_LAMBDA RETURN_LAST
LAMBDA_MARKER RETURN_LAST
""".split())
if 2.6 <= PYTHON_VERSION <= 2.7:
opcode_set = set(s.opc.opname).union(ignore_set)

View File

@@ -1,168 +0,0 @@
from uncompyle6 import PYTHON3
from uncompyle6.semantics.consts import (
escape, NONE,
# RETURN_NONE, PASS, RETURN_LOCALS
)
if PYTHON3:
from io import StringIO
def iteritems(d):
return d.items()
else:
from StringIO import StringIO
def iteritems(d):
return d.iteritems()
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_DIRECT, TABLE_R,
)
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 iteritems(t):
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':
assert isinstance(entry[arg], int), (
"%s[%s][%d] kind %s is '%s' should be an int but is %s. "
"Full entry: %s" %
(name, k, arg, typ, entry[arg], type(entry[arg]), 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))

View File

@@ -6,8 +6,7 @@ import difflib
import subprocess
import tempfile
import functools
# compatability
import six
from StringIO import StringIO
# uncompyle6 / xdis
from uncompyle6 import PYTHON_VERSION, IS_PYPY, deparse_code
# TODO : I think we can get xdis to support the dis api (python 3 version) by doing something like this there
@@ -123,9 +122,7 @@ def validate_uncompyle(text, mode='exec'):
original_text = text
deparsed = deparse_code(PYTHON_VERSION, original_code,
compile_mode=mode,
out=six.StringIO(),
is_pypy=IS_PYPY)
compile_mode=mode, out=StringIO())
uncompyled_text = deparsed.text
uncompyled_code = compile(uncompyled_text, '<string>', 'exec')

View File

@@ -24,7 +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,
python_requires='>=2.6, !=3.0.*, !=3.1.*, !=3.2.*, <4',
zip_safe = zip_safe)

View File

@@ -19,7 +19,7 @@ check:
$(MAKE) check-$(PYTHON_VERSION)
#: Run working tests from Python 2.6 or 2.7
check-2.6 check-2.7: check-bytecode-2 check-bytecode-3 check-bytecode-1 check-native-short
check-2.4 check-2.5 check-2.6 check-2.7: check-bytecode-2 check-bytecode-3 check-bytecode-1 check-native-short
#: Run working tests from Python 3.0
check-3.0: check-bytecode
@@ -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 --weak-verify $(COMPILE)
$(PYTHON) test_pythonlib.py --bytecode-3.4 --verify $(COMPILE)
#: Run working tests from Python 3.5
check-3.5: check-bytecode
@@ -47,7 +47,7 @@ check-3.5: check-bytecode
#: Run working tests from Python 3.6
check-3.6: check-bytecode
$(PYTHON) test_pythonlib.py --bytecode-3.6 --weak-verify $(COMPILE)
$(PYTHON) test_pythonlib.py --bytecode-3.6 --verify $(COMPILE)
#: Check deparsing only, but from a different Python version
check-disasm:
@@ -67,7 +67,7 @@ check-bytecode-2:
check-bytecode-3:
$(PYTHON) test_pythonlib.py --bytecode-3.0 \
--bytecode-3.1 --bytecode-3.2 --bytecode-3.3 \
--bytecode-3.4 --bytecode-3.5 --bytecode-pypy3.2
--bytecode-3.4 --bytecode-3.5 --bytecode-3.6 --bytecode-pypy3.2
#: Check deparsing bytecode that works running Python 2 and Python 3
check-bytecode: check-bytecode-3
@@ -97,29 +97,6 @@ check-bytecode-2.4:
check-bytecode-2.5:
$(PYTHON) test_pythonlib.py --bytecode-2.5
#: Get grammar coverage for Python 2.5
grammar-coverage-2.5:
SPARK_PARSER_COVERAGE=/tmp/spark-grammar-25.cover $(PYTHON) test_pythonlib.py --bytecode-2.5
SPARK_PARSER_COVERAGE=/tmp/spark-grammar-25.cover $(PYTHON) test_pyenvlib.py --2.5.6
#: Get grammar coverage for Python 2.6
grammar-coverage-2.6:
SPARK_PARSER_COVERAGE=/tmp/spark-grammar-26.cover $(PYTHON) test_pythonlib.py --bytecode-2.6
SPARK_PARSER_COVERAGE=/tmp/spark-grammar-26.cover $(PYTHON) test_pyenvlib.py --2.6.9
#: Get grammar coverage for Python 2.7
grammar-coverage-2.7:
SPARK_PARSER_COVERAGE=/tmp/spark-grammar-27.cover $(PYTHON) test_pythonlib.py --bytecode-2.7
SPARK_PARSER_COVERAGE=/tmp/spark-grammar-27.cover $(PYTHON) test_pyenvlib.py --2.7.13
#: Check deparsing Python 2.6
check-bytecode-2.6:
$(PYTHON) test_pythonlib.py --bytecode-2.6 --weak-verify
#: Check deparsing Python 2.7
check-bytecode-2.7:
$(PYTHON) test_pythonlib.py --bytecode-2.7 --verify
#: Check deparsing Python 3.0
check-bytecode-3.0:
$(PYTHON) test_pythonlib.py --bytecode-3.0
@@ -148,9 +125,33 @@ check-bytecode-3.5:
check-bytecode-3.6:
$(PYTHON) test_pythonlib.py --bytecode-3.6
#: Get grammar coverage for Python 2.4
grammar-coverage-2.4:
SPARK_PARSER_COVERAGE=/tmp/spark-grammar-24.cover $(PYTHON) test_pythonlib.py --bytecode-2.4
SPARK_PARSER_COVERAGE=/tmp/spark-grammar-24.cover $(PYTHON) test_pyenvlib.py --2.4.6
#: Get grammar coverage for Python 2.5
grammar-coverage-2.5:
SPARK_PARSER_COVERAGE=/tmp/spark-grammar-25.cover $(PYTHON) test_pythonlib.py --bytecode-2.5
SPARK_PARSER_COVERAGE=/tmp/spark-grammar-25.cover $(PYTHON) test_pyenvlib.py --2.5.6
#: Get grammar coverage for Python 2.6
grammar-coverage-2.6:
SPARK_PARSER_COVERAGE=/tmp/spark-grammar-26.cover $(PYTHON) test_pythonlib.py --bytecode-2.6
SPARK_PARSER_COVERAGE=/tmp/spark-grammar-26.cover $(PYTHON) test_pyenvlib.py --2.6.9
#: Get grammar coverage for Python 2.7
grammar-coverage-2.7:
SPARK_PARSER_COVERAGE=/tmp/spark-grammar-27.cover $(PYTHON) test_pythonlib.py --bytecode-2.7
SPARK_PARSER_COVERAGE=/tmp/spark-grammar-27.cover $(PYTHON) test_pyenvlib.py --2.7.13
#: short tests for bytecodes only for this version of Python
check-native-short:
$(PYTHON) test_pythonlib.py --bytecode-$(PYTHON_VERSION) --verify $(COMPILE)
$(PYTHON) test_pythonlib.py --bytecode-$(PYTHON_VERSION) --weak-verify $(COMPILE)
#: Run longer Python 2.6's lib files known to be okay
check-2.4-ok:
$(PYTHON) test_pythonlib.py --ok-2.4 --verify $(COMPILE)
#: Run longer Python 2.6's lib files known to be okay
check-2.6-ok:

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,16 +0,0 @@
# 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)
# 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()

View File

@@ -1,19 +0,0 @@
# Bug in < 2.6 is having a COME_FROM_LOOP (but we
# don't tag that so it is just COME_FROM *before*
# a jump back to the loop.
def pickup(self, open_players, open_buf, wrap_buf):
for aplayer in self._game.active_players:
if aplayer in open_players:
aplayer.send(open_players)
if self == aplayer:
for awatcher in self._watchers:
if awatcher._can_see_detail:
awatcher.send(open_buf)
else:
awatcher.send(wrap_buf)
else:
self._game.send(aplayer.side)
else:
self._game.send(aplayer.side, wrap_buf)

View File

@@ -9,7 +9,7 @@ def open(file, mode = "r", buffering = None,
newline = None, closefd = True) -> "IOBase":
return text
def foo1(x: 'an argument that defaults to 5' = 5):
def foo(x: 'an argument that defaults to 5' = 5):
print(x)
def div(a: dict(type=float, help='the dividend'),

View File

@@ -19,8 +19,6 @@ Step 2: Run the test:
test_pyenvlib --mylib --verify # decompile verify 'mylib'
"""
from __future__ import print_function
from uncompyle6 import main, PYTHON3
import os, time, shutil, sys
from fnmatch import fnmatch
@@ -29,7 +27,7 @@ from fnmatch import fnmatch
TEST_VERSIONS=('2.3.7', '2.4.6', '2.5.6', '2.6.9',
'pypy-2.4.0', 'pypy-2.6.1',
'pypy-5.0.1', 'pypy-5.3.1', 'pypy3.5-5.7.1-beta',
'pypy-5.0.1', 'pypy-5.3.1',
'2.7.10', '2.7.11', '2.7.12', '2.7.13',
'3.0.1', '3.1.5', '3.2.6',
'3.3.5', '3.3.6',

View File

@@ -27,8 +27,6 @@ Step 2: Run the test:
test_pythonlib.py --mylib --verify # decompile verify 'mylib'
"""
from __future__ import print_function
import getopt, os, py_compile, sys, shutil, tempfile, time
from uncompyle6 import PYTHON_VERSION
@@ -127,8 +125,10 @@ def do_tests(src_dir, obj_patterns, target_dir, opts):
if opts['do_compile']:
compiled_version = opts['compiled_version']
if compiled_version and PYTHON_VERSION != compiled_version:
print("Not compiling: desired Python version is %s but we are running %s" %
(compiled_version, PYTHON_VERSION), file=sys.stderr)
sys.stderr.write("Not compiling: "
"desired Python version is %s "
"but we are running %s" %
(compiled_version, PYTHON_VERSION))
else:
for root, dirs, basenames in os.walk(src_dir):
file_matches(files, root, basenames, PY)
@@ -146,8 +146,8 @@ def do_tests(src_dir, obj_patterns, target_dir, opts):
file_matches(files, dirname, basenames, obj_patterns)
if not files:
print("Didn't come up with any files to test! Try with --compile?",
file=sys.stderr)
sys.stderr.write("Didn't come up with any files to test! "
"Try with --compile?")
exit(1)
os.chdir(cwd)
@@ -161,21 +161,21 @@ def do_tests(src_dir, obj_patterns, target_dir, opts):
except ValueError:
pass
print(time.ctime())
print('Source directory: ', src_dir)
print('Output directory: ', target_dir)
print time.ctime()
print 'Source directory: ', src_dir
print 'Output directory: ', target_dir
try:
_, _, failed_files, failed_verify = \
main(src_dir, target_dir, files, [],
do_verify=opts['do_verify'])
if failed_files != 0:
sys.exit(2)
exit(2)
elif failed_verify != 0:
sys.exit(3)
exit(3)
except (KeyboardInterrupt, OSError):
print()
sys.exit(1)
exit(1)
if test_opts['rmtree']:
parent_dir = os.path.dirname(target_dir)
print("Everything good, removing %s" % parent_dir)
@@ -236,14 +236,13 @@ if __name__ == '__main__':
if os.path.isdir(src_dir):
checked_dirs.append([src_dir, pattern, target_dir])
else:
print("Can't find directory %s. Skipping" % src_dir,
file=sys.stderr)
sys.stderr.write("Can't find directory %s. Skipping" % src_dir)
continue
last_compile_version = compiled_version
pass
if not checked_dirs:
print("No directories found to check", file=sys.stderr)
sys.stderr.write("No directories found to check\n")
sys.exit(1)
test_opts['compiled_version'] = last_compile_version

View File

@@ -3,7 +3,6 @@
#
# Copyright (c) 2015-2016 by Rocky Bernstein <rb@dustyfeet.com>
#
from __future__ import print_function
import sys, os, getopt
from uncompyle6.disas import disassemble_file
@@ -26,7 +25,7 @@ Options:
-V | --version show version and stop
-h | --help show this message
""".format(program)
""" % (program, program)
PATTERNS = ('*.pyc', '*.pyo')
@@ -37,15 +36,15 @@ Type -h for for full help.""" % program
native = True
if len(sys.argv) == 1:
print("No file(s) given", file=sys.stderr)
print(Usage_short, file=sys.stderr)
sys.stderr.write("No file(s) given\n")
sys.stderr.write(Usage_short)
sys.exit(1)
try:
opts, files = getopt.getopt(sys.argv[1:], 'hVU',
['help', 'version', 'uncompyle6'])
except getopt.GetoptError as e:
print('%s: %s' % (os.path.basename(sys.argv[0]), e), file=sys.stderr)
except getopt.GetoptError(e):
sys.stderr.write('%s: %s' % (os.path.basename(sys.argv[0]), e))
sys.exit(-1)
for opt, val in opts:
@@ -59,15 +58,14 @@ Type -h for for full help.""" % program
native = False
else:
print(opt)
print(Usage_short, file=sys.stderr)
sys.stderr.write(Usage_short)
sys.exit(1)
for file in files:
if os.path.exists(files[0]):
disassemble_file(file, sys.stdout, native)
else:
print("Can't read %s - skipping" % files[0],
file=sys.stderr)
sys.stderr.write("Can't read %s - skipping\n" % files[0])
pass
pass
return

View File

@@ -4,7 +4,6 @@
# Copyright (c) 2015-2016 by Rocky Bernstein
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
#
from __future__ import print_function
import sys, os, getopt, time
program, ext = os.path.splitext(os.path.basename(__file__))
@@ -65,11 +64,11 @@ def usage():
def main_bin():
if not (sys.version_info[0:2] in ((2, 6), (2, 7),
(3, 1), (3, 2), (3, 3),
if not (sys.version_info[0:2] in ((2, 4), (2, 5), (2, 6), (2, 7),
(3, 2), (3, 3),
(3, 4), (3, 5), (3, 6))):
print('Error: %s requires Python 2.6-2.7, or 3.1-3.6' % program,
file=sys.stderr)
sys.stderr.write('Error: %s requires Python 2.4 2.5 2.6, 2.7, '
'3.2, 3.3, 3.4, 3.5, or 3.6' % program)
sys.exit(-1)
do_verify = recurse_dirs = False
@@ -84,8 +83,8 @@ def main_bin():
opts, files = getopt.getopt(sys.argv[1:], 'hagtdrVo:c:p:',
'help asm grammar linemaps recurse timestamp tree '
'verify version showgrammar'.split(' '))
except getopt.GetoptError as e:
print('%s: %s' % (os.path.basename(sys.argv[0]), e), file=sys.stderr)
except getopt.GetoptError(e):
sys.stderr.write('%s: %s\n' % (os.path.basename(sys.argv[0]), e))
sys.exit(-1)
options = {}
@@ -119,7 +118,7 @@ def main_bin():
elif opt in ('--recurse', '-r'):
recurse_dirs = True
else:
print(opt, file=sys.stderr)
sys.stderr.write(opt)
usage()
# expand directory if specified
@@ -144,7 +143,7 @@ def main_bin():
files = [f[sb_len:] for f in files]
if not files:
print("No files given", file=sys.stderr)
sys.stderr.write("No files given\n")
usage()
if outfile == '-':

View File

@@ -16,8 +16,6 @@ Second, we need structured instruction information for the
want to run on Python 2.7.
"""
from __future__ import print_function
import sys
from collections import deque
@@ -37,10 +35,9 @@ def disco(version, co, out=None, is_pypy=False):
# store final output stream for case of error
real_out = out or sys.stdout
print('# Python %s' % version, file=real_out)
real_out.write('# Python %s\n' % version)
if co.co_filename:
print('# Embedded file name: %s' % co.co_filename,
file=real_out)
real_out.write('# Embedded file name: %s\n' % co.co_filename)
scanner = get_scanner(version, is_pypy=is_pypy)
@@ -52,16 +49,15 @@ def disco_loop(disasm, queue, real_out):
while len(queue) > 0:
co = queue.popleft()
if co.co_name != '<module>':
print('\n# %s line %d of %s' %
(co.co_name, co.co_firstlineno, co.co_filename),
file=real_out)
real_out.write('\n# %s line %d of %s\n' %
(co.co_name, co.co_firstlineno, co.co_filename))
tokens, customize = disasm(co)
for t in tokens:
if iscode(t.pattr):
queue.append(t.pattr)
elif iscode(t.attr):
queue.append(t.attr)
print(t, file=real_out)
real_out.write(t)
pass
pass

View File

@@ -10,7 +10,7 @@ def line_number_mapping(pyc_filename, src_filename):
source_size) = load_module(pyc_filename)
try:
code2 = load_file(src_filename)
except SyntaxError as e:
except SyntaxError, e:
return str(e)
queue = deque([code1, code2])

View File

@@ -1,4 +1,3 @@
from __future__ import print_function
import datetime, os, subprocess, sys, tempfile
from uncompyle6 import verify, IS_PYPY
@@ -22,31 +21,36 @@ def decompile(
# store final output stream for case of error
real_out = out or sys.stdout
co_pypy_str = 'PyPy ' if is_pypy else ''
run_pypy_str = 'PyPy ' if IS_PYPY else ''
print('# uncompyle6 version %s\n'
'# %sPython bytecode %s%s\n# Decompiled from: %sPython %s' %
(VERSION, co_pypy_str, bytecode_version,
" (%d)" % magic_int if magic_int else "",
run_pypy_str, '\n# '.join(sys.version.split('\n'))),
file=real_out)
if co.co_filename:
print('# Embedded file name: %s' % co.co_filename,
file=real_out)
if timestamp:
print('# Compiled at: %s' % datetime.datetime.fromtimestamp(timestamp),
file=real_out)
if source_size:
print('# Size of source mod 2**32: %d bytes' % source_size,
file=real_out)
if is_pypy:
co_pypy_str = 'PyPy '
else:
co_pypy_str = ''
try:
pysource.deparse_code(bytecode_version, co, out, showasm, showast,
showgrammar, code_objects=code_objects,
is_pypy=is_pypy)
except pysource.SourceWalkerError as e:
# deparsing failed
raise pysource.SourceWalkerError(str(e))
if IS_PYPY:
run_pypy_str = 'PyPy '
else:
run_pypy_str = ''
if magic_int:
m = str(magic_int)
else:
m = ""
real_out.write('# uncompyle6 version %s\n'
'# %sPython bytecode %s%s\n# Decompiled from: %sPython %s\n' %
(VERSION, co_pypy_str, bytecode_version,
" (%s)" % m, run_pypy_str,
'\n# '.join(sys.version.split('\n'))))
if co.co_filename:
real_out.write('# Embedded file name: %s\n' % co.co_filename)
if timestamp:
real_out.write('# Compiled at: %s\n' %
datetime.datetime.fromtimestamp(timestamp))
if source_size:
real_out.write('# Size of source mod 2**32: %d bytes\n' % source_size)
pysource.deparse_code(bytecode_version, co, out, showasm, showast,
showgrammar, code_objects=code_objects,
is_pypy=is_pypy)
# For compatiblity
uncompyle = decompile
@@ -128,7 +132,10 @@ def main(in_base, out_base, files, codes, outfile=None,
junk, outfile = tempfile.mkstemp(suffix=".py",
prefix=prefix)
# Unbuffer output if possible
buffering = -1 if sys.stdout.isatty() else 0
if sys.stdout.isatty():
buffering = -1
else:
buffering = 0
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', buffering)
tee = subprocess.Popen(["tee", outfile], stdin=subprocess.PIPE)
os.dup2(tee.stdin.fileno(), sys.stdout.fileno())
@@ -145,9 +152,9 @@ def main(in_base, out_base, files, codes, outfile=None,
try:
decompile_file(infile, outstream, showasm, showast, showgrammar)
tot_files += 1
except (ValueError, SyntaxError, ParserError, pysource.SourceWalkerError) as e:
except (ValueError, SyntaxError, ParserError, pysource.SourceWalkerError):
sys.stdout.write("\n")
sys.stderr.write("\n# file %s\n# %s\n" % (infile, e))
sys.stderr.write("# file %s\n" % (infile))
failed_files += 1
except KeyboardInterrupt:
if outfile:
@@ -181,24 +188,28 @@ def main(in_base, out_base, files, codes, outfile=None,
msg = verify.compare_code_with_srcfile(infile, current_outfile, weak_verify=weak_verify)
if not current_outfile:
if not msg:
print('\n# okay decompiling %s' % infile)
print '\n# okay decompiling %s' % infile
okay_files += 1
else:
print('\n# %s\n\t%s', infile, msg)
except verify.VerifyCmpError as e:
print '\n# %s\n\t%s', infile, msg
except verify.VerifyCmpError, e:
print(e)
verify_failed_files += 1
os.rename(current_outfile, current_outfile + '_unverified')
sys.stderr.write("### Error Verifying %s\n" % filename)
sys.stderr.write(str(e) + "\n")
if not outfile:
sys.stder.write("### Error Verifiying %s" %
filename)
sys.stderr.write(e)
if raise_on_error:
raise
pass
pass
pass
elif do_verify:
sys.stderr.write("\n### uncompile successful, but no file to compare against\n")
sys.stderr.write("\n### uncompile successful, "
"but no file to compare against")
pass
else:
okay_files += 1

View File

@@ -6,15 +6,13 @@
Common uncompyle parser routines.
"""
from __future__ import print_function
import sys
from xdis.code import iscode
from xdis.magics import py_str2float
from spark_parser import GenericASTBuilder, DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
from uncompyle6.show import maybe_show_asm
class ParserError(Exception):
def __init__(self, token, offset):
self.token = token
@@ -30,13 +28,13 @@ class PythonParser(GenericASTBuilder):
def __init__(self, AST, start, debug):
super(PythonParser, self).__init__(AST, start, debug)
self.collect = frozenset(
['stmts', 'except_stmts', '_stmts', 'load_attrs',
'exprlist', 'kvlist', 'kwargs', 'come_froms', '_come_from',
# Python < 3
'print_items',
# PyPy:
'kvlist_n'])
self.collect = [
'stmts', 'except_stmts', '_stmts', 'load_attrs',
'exprlist', 'kvlist', 'kwargs', 'come_froms', '_come_from',
# Python < 3
'print_items',
# PyPy:
'kvlist_n']
def ast_first_offset(self, ast):
if hasattr(ast, 'offset'):
@@ -44,25 +42,21 @@ class PythonParser(GenericASTBuilder):
else:
return self.ast_first_offset(ast[0])
def add_unique_rule(self, rule, opname, arg_count, customize):
def add_unique_rule(self, rule, opname, count, customize):
"""Add rule to grammar, but only if it hasn't been added previously
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.
opname and count are used in the customize() semantic the actions
to add the semantic action rule. Often, count 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] = arg_count
customize[opname] = count
pass
return
def add_unique_rules(self, rules, customize):
"""Add rules (a list of string) to grammar. Note that
the rules must not be those that set arg_count in the
custom dictionary.
"""Add rules (a list of string) to grammar
"""
for rule in rules:
if len(rule) == 0:
@@ -72,9 +66,7 @@ class PythonParser(GenericASTBuilder):
return
def add_unique_doc_rules(self, rules_str, customize):
"""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.
"""Add rules (a docstring-like list of rules) to grammar
"""
rules = [r.strip() for r in rules_str.split("\n")]
self.add_unique_rules(rules, customize)
@@ -91,14 +83,17 @@ class PythonParser(GenericASTBuilder):
for i in dir(self):
setattr(self, i, None)
def debug_reduce(self, rule, tokens, parent, last_token_pos):
def debug_reduce(self, rule, tokens, parent, i):
"""Customized format and print for our kind of tokens
which gets called in debugging grammar reduce rules
"""
def fix(c):
s = str(c)
last_token_pos = s.find('_')
return s if last_token_pos == -1 else s[:last_token_pos]
i = s.find('_')
if i == -1:
return s
else:
return s[:i]
prefix = ''
if parent and tokens:
@@ -110,13 +105,13 @@ class PythonParser(GenericASTBuilder):
if hasattr(p_token, 'offset'):
prefix += "%3s" % fix(p_token.offset)
if len(rule[1]) > 1:
prefix += '-%-3s ' % fix(tokens[last_token_pos-1].offset)
prefix += '-%-3s ' % fix(tokens[i-1].offset)
else:
prefix += ' '
else:
prefix = ' '
print("%s%s ::= %s (%d)" % (prefix, rule[0], ' '.join(rule[1]), last_token_pos))
print("%s%s ::= %s" % (prefix, rule[0], ' '.join(rule[1])))
def error(self, instructions, index):
# Find the last line boundary
@@ -129,12 +124,15 @@ class PythonParser(GenericASTBuilder):
err_token = instructions[index]
print("Instruction context:")
for i in range(start, finish):
indent = ' ' if i != index else '-> '
if i != index:
indent = ' '
else:
indent = '-> '
print("%s%s" % (indent, instructions[i]))
raise ParserError(err_token, err_token.offset)
def typestring(self, token):
return token.kind
return token.type
def nonterminal(self, nt, args):
if nt in self.collect and len(args) > 1:
@@ -256,11 +254,8 @@ class PythonParser(GenericASTBuilder):
stmt ::= return_stmt
return_stmt ::= ret_expr RETURN_VALUE
return_stmt_lambda ::= ret_expr RETURN_VALUE_LAMBDA
return_stmts ::= return_stmt
return_stmts ::= _stmts return_stmt
"""
pass
@@ -535,9 +530,7 @@ class PythonParser(GenericASTBuilder):
stmt ::= return_lambda
stmt ::= conditional_lambda
return_lambda ::= ret_expr RETURN_VALUE_LAMBDA LAMBDA_MARKER
return_lambda ::= ret_expr RETURN_VALUE_LAMBDA
return_lambda ::= ret_expr RETURN_VALUE LAMBDA_MARKER
conditional_lambda ::= expr jmp_false return_if_stmt return_stmt LAMBDA_MARKER
cmp ::= cmp_list
@@ -616,15 +609,7 @@ def get_python_parser(
explanation of the different modes.
"""
# If version is a string, turn that into the corresponding float.
if isinstance(version, str):
version = py_str2float(version)
# FIXME: there has to be a better way...
# We could do this as a table lookup, but that would force us
# in import all of the parsers all of the time. Perhaps there is
# a lazy way of doing the import?
if version < 3.0:
if version == 1.5:
import uncompyle6.parsers.parse15 as parse15
@@ -733,7 +718,7 @@ def get_python_parser(
else:
p = parse3.Python3ParserSingle(debug_parser)
p.version = version
# p.dump_grammar() # debug
# p.dumpGrammar() # debug
return p
class PythonParserSingle(PythonParser):
@@ -777,7 +762,6 @@ def python_parser(version, co, out=sys.stdout, showasm=False,
if __name__ == '__main__':
def parse_test(co):
from uncompyle6 import PYTHON_VERSION, IS_PYPY
ast = python_parser('2.7.13', co, showasm=True, is_pypy=True)
ast = python_parser(PYTHON_VERSION, co, showasm=True, is_pypy=IS_PYPY)
print(ast)
return

View File

@@ -16,7 +16,7 @@ class AST(spark_AST):
return self.__repr1__('', None)
def __repr1__(self, indent, sibNum=None):
rv = str(self.kind)
rv = str(self.type)
if sibNum is not None:
rv = "%2d. %s" % (sibNum, rv)
enumerate_children = False

View File

@@ -29,8 +29,8 @@ class Python15ParserSingle(Python21Parser, PythonParserSingle):
if __name__ == '__main__':
# Check grammar
p = Python15Parser()
p.check_grammar()
p.dump_grammar()
p.checkGrammar()
p.dumpGrammar()
# local variables:
# tab-width: 4

View File

@@ -12,8 +12,6 @@ If we succeed in creating a parse tree, then we have a Python program
that a later phase can turn into a sequence of ASCII text.
"""
from __future__ import print_function
from uncompyle6.parser import PythonParser, PythonParserSingle, nop_func
from uncompyle6.parsers.astnode import AST
from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
@@ -417,4 +415,4 @@ class Python2ParserSingle(Python2Parser, PythonParserSingle):
if __name__ == '__main__':
# Check grammar
p = Python2Parser()
p.check_grammar()
p.checkGrammar()

View File

@@ -33,8 +33,8 @@ class Python21ParserSingle(Python22Parser, PythonParserSingle):
if __name__ == '__main__':
# Check grammar
p = Python21Parser()
p.check_grammar()
p.dump_grammar()
p.checkGrammar()
p.dumpGrammar()
# local variables:
# tab-width: 4

View File

@@ -26,8 +26,8 @@ class Python22ParserSingle(Python23Parser, PythonParserSingle):
if __name__ == '__main__':
# Check grammar
p = Python22Parser()
p.check_grammar()
p.dump_grammar()
p.checkGrammar()
p.dumpGrammar()
# local variables:
# tab-width: 4

View File

@@ -67,8 +67,8 @@ class Python23ParserSingle(Python23Parser, PythonParserSingle):
if __name__ == '__main__':
# Check grammar
p = Python23Parser()
p.check_grammar()
p.dump_grammar()
p.checkGrammar()
p.dumpGrammar()
# local variables:
# tab-width: 4

View File

@@ -58,11 +58,10 @@ class Python24Parser(Python25Parser):
if invalid:
return invalid
# FiXME: this code never gets called...
lhs = rule[0]
if lhs == 'nop_stmt':
l = len(tokens)
if 0 <= l < len(tokens):
return not int(tokens[first].pattr) == tokens[last].offset
return not int(tokens[first].pattr) == tokens[last].offset
return False
@@ -72,4 +71,4 @@ class Python24ParserSingle(Python24Parser, PythonParserSingle):
if __name__ == '__main__':
# Check grammar
p = Python24Parser()
p.check_grammar()
p.checkGrammar()

View File

@@ -60,4 +60,4 @@ class Python25ParserSingle(Python26Parser, PythonParserSingle):
if __name__ == '__main__':
# Check grammar
p = Python25Parser()
p.check_grammar()
p.checkGrammar()

View File

@@ -84,12 +84,6 @@ class Python26Parser(Python2Parser):
ja_cf_pop ::= JUMP_ABSOLUTE come_froms POP_TOP
jf_cf_pop ::= JUMP_FORWARD come_froms POP_TOP
# The first optional COME_FROM when it appears is really
# COME_FROM_LOOP, but in <= 2.6 we don't distinguish
# this
cf_jb_cf_pop ::= _come_from JUMP_BACK come_froms POP_TOP
bp_come_from ::= POP_BLOCK COME_FROM
jb_bp_come_from ::= JUMP_BACK bp_come_from
@@ -117,8 +111,7 @@ class Python26Parser(Python2Parser):
break_stmt ::= BREAK_LOOP JUMP_BACK
# Semantic actions want else_suitel to be at index 3
ifelsestmtl ::= testexpr c_stmts_opt cf_jb_cf_pop else_suitel
ifelsestmtl ::= testexpr c_stmts_opt jb_cf_pop else_suitel
ifelsestmtc ::= testexpr c_stmts_opt ja_cf_pop else_suitec
# Semantic actions want suite_stmts_opt to be at index 3
@@ -247,9 +240,7 @@ 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
return_if_lambda ::= RETURN_END_IF_LAMBDA POP_TOP
conditional_lambda ::= expr jmp_false_then expr return_if_lambda
return_stmt_lambda LAMBDA_MARKER
conditional_lambda ::= expr jmp_false_then return_if_stmt return_stmt LAMBDA_MARKER
"""
def add_custom_rules(self, tokens, customize):
@@ -276,10 +267,10 @@ class Python26ParserSingle(Python2Parser, PythonParserSingle):
if __name__ == '__main__':
# Check grammar
p = Python26Parser()
p.check_grammar()
p.checkGrammar()
from uncompyle6 import PYTHON_VERSION, IS_PYPY
if PYTHON_VERSION == 2.6:
lhs, rhs, tokens, right_recursive = p.check_sets()
lhs, rhs, tokens, right_recursive = p.checkSets()
from uncompyle6.scanner import get_scanner
s = get_scanner(PYTHON_VERSION, IS_PYPY)
opcode_set = set(s.opc.opname).union(set(

View File

@@ -94,10 +94,6 @@ 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
"""
@@ -129,10 +125,10 @@ class Python27ParserSingle(Python27Parser, PythonParserSingle):
if __name__ == '__main__':
# Check grammar
p = Python27Parser()
p.check_grammar()
p.checkGrammar()
from uncompyle6 import PYTHON_VERSION, IS_PYPY
if PYTHON_VERSION == 2.7:
lhs, rhs, tokens, right_recursive = p.check_sets()
lhs, rhs, tokens, right_recursive = p.checkSets()
from uncompyle6.scanner import get_scanner
s = get_scanner(PYTHON_VERSION, IS_PYPY)
opcode_set = set(s.opc.opname).union(set(
@@ -148,4 +144,4 @@ if __name__ == '__main__':
for t in remain_tokens])
remain_tokens = set(remain_tokens) - opcode_set
print(remain_tokens)
# p.dump_grammar()
# p.dumpGrammar()

View File

@@ -15,12 +15,9 @@ If we succeed in creating a parse tree, then we have a Python program
that a later phase can turn into a sequence of ASCII text.
"""
from __future__ import print_function
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):
@@ -157,13 +154,8 @@ 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
# passstmt _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
ifelsestmtc ::= testexpr c_stmts_opt JUMP_ABSOLUTE else_suitec
ifelsestmtc ::= testexpr c_stmts_opt jump_absolute_else else_suitec
@@ -259,14 +251,8 @@ 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):
@@ -429,13 +415,6 @@ 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+
@@ -446,7 +425,7 @@ class Python3Parser(PythonParser):
@staticmethod
def call_fn_name(token):
"""Customize CALL_FUNCTION to add the number of positional arguments"""
return '%s_%i' % (token.kind, token.attr)
return '%s_%i' % (token.type, token.attr)
def custom_build_class_rule(self, opname, i, token, tokens, customize):
'''
@@ -462,16 +441,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].kind.startswith('MAKE_FUNCTION'):
if tokens[i].type.startswith('MAKE_FUNCTION'):
break
elif tokens[i].kind.startswith('MAKE_CLOSURE'):
elif tokens[i].type.startswith('MAKE_CLOSURE'):
break
pass
assert i < len(tokens), "build_class needs to find MAKE_FUNCTION or MAKE_CLOSURE"
assert tokens[i+1].kind == 'LOAD_CONST', \
assert tokens[i+1].type == 'LOAD_CONST', \
"build_class expecting CONST after MAKE_FUNCTION/MAKE_CLOSURE"
for i in range(i, len(tokens)):
if tokens[i].kind == 'CALL_FUNCTION':
if tokens[i].type == 'CALL_FUNCTION':
call_fn_tok = tokens[i]
break
assert call_fn_tok, "build_class custom rule needs to find CALL_FUNCTION"
@@ -512,7 +491,7 @@ class Python3Parser(PythonParser):
# Yes, this computation based on instruction name is a little bit hoaky.
nak = ( len(opname)-len('CALL_FUNCTION') ) // 3
token.kind = self.call_fn_name(token)
token.type = 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
@@ -524,39 +503,42 @@ class Python3Parser(PythonParser):
kw = ''
rule = ('call_function ::= expr expr ' +
('pos_arg ' * args_pos) +
('kwarg ' * args_kw) + kw + token.kind)
self.add_unique_rule(rule, token.kind, uniq_param, customize)
('kwarg ' * args_kw) + kw + token.type)
self.add_unique_rule(rule, token.type, 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.kind, uniq_param, customize)
self.add_unique_rule(rule, token.type, uniq_param, customize)
rule = 'call_function ::= call_function36'
else:
rule = ('call_function ::= expr ' +
('pos_arg ' * args_pos) +
('kwarg ' * args_kw) +
'expr ' * nak + token.kind)
'expr ' * nak + token.type)
self.add_unique_rule(rule, token.kind, uniq_param, customize)
self.add_unique_rule(rule, token.type, uniq_param, customize)
if self.version >= 3.5:
rule = ('async_call_function ::= expr ' +
('pos_arg ' * args_pos) +
('kwarg ' * args_kw) +
'expr ' * nak + token.kind +
'expr ' * nak + token.type +
' GET_AWAITABLE LOAD_CONST YIELD_FROM')
self.add_unique_rule(rule, token.kind, uniq_param, customize)
self.add_unique_rule('expr ::= async_call_function', token.kind, uniq_param, customize)
self.add_unique_rule(rule, token.type, uniq_param, customize)
self.add_unique_rule('expr ::= async_call_function', token.type, 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.kind, uniq_param, customize)
self.add_unique_rule(rule, token.type, 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
this has an effect on many rules.
"""
new_rule = rule % (('LOAD_CONST ') * (1 if self.version >= 3.3 else 0))
if self.version >= 3.3:
new_rule = rule % (('LOAD_CONST ') * 1)
else:
new_rule = rule % (('LOAD_CONST ') * 0)
self.add_unique_rule(new_rule, opname, attr, customize)
def add_custom_rules(self, tokens, customize):
@@ -624,7 +606,7 @@ class Python3Parser(PythonParser):
call_function ::= expr CALL_METHOD
"""
for i, token in enumerate(tokens):
opname = token.kind
opname = token.type
opname_base = opname[:opname.rfind('_')]
if opname == 'PyPy':
@@ -908,11 +890,8 @@ class Python3Parser(PythonParser):
elif lhs == 'annotate_tuple':
return not isinstance(tokens[first].attr, tuple)
elif lhs == 'kwarg':
arg = tokens[first].attr
if PYTHON3:
return not isinstance(arg, str)
else:
return not (isinstance(arg, str) or isinstance(arg, unicode))
return not (isinstance(tokens[first].attr, unicode) or
isinstance(tokens[first].attr, str))
elif lhs == 'while1elsestmt':
# if SETUP_LOOP target spans the else part, then this is
# not while1else. Also do for whileTrue?
@@ -921,8 +900,7 @@ class Python3Parser(PythonParser):
last += 1
return tokens[first].attr == tokens[last].offset
elif lhs == 'while1stmt':
if (0 <= last < len(tokens)
and tokens[last] in ('COME_FROM_LOOP', 'JUMP_BACK')):
if 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):
@@ -966,10 +944,10 @@ def info(args):
p = Python32Parser()
elif arg == '3.0':
p = Python30Parser()
p.check_grammar()
p.checkGrammar()
if len(sys.argv) > 1 and sys.argv[1] == 'dump':
print('-' * 50)
p.dump_grammar()
p.dumpGrammar()
if __name__ == '__main__':
import sys

View File

@@ -2,7 +2,6 @@
"""
spark grammar differences over Python 3.1 for Python 3.0.
"""
from __future__ import print_function
from uncompyle6.parser import PythonParserSingle
from uncompyle6.parsers.parse31 import Python31Parser

View File

@@ -2,7 +2,6 @@
"""
spark grammar differences over Python 3.2 for Python 3.1.
"""
from __future__ import print_function
from uncompyle6.parser import PythonParserSingle
from uncompyle6.parsers.parse32 import Python32Parser

View File

@@ -2,8 +2,6 @@
"""
spark grammar differences over Python 3 for Python 3.2.
"""
from __future__ import print_function
from uncompyle6.parser import PythonParserSingle
from uncompyle6.parsers.parse3 import Python3Parser
@@ -44,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.kind
opname = token.type
if opname.startswith('MAKE_FUNCTION_A'):
args_pos, args_kw, annotate_args = token.attr
# Check that there are 2 annotated params?

View File

@@ -2,7 +2,6 @@
"""
spark grammar differences over Python 3.2 for Python 3.3.
"""
from __future__ import print_function
from uncompyle6.parser import PythonParserSingle
from uncompyle6.parsers.parse32 import Python32Parser

View File

@@ -29,10 +29,10 @@ class Python34ParserSingle(Python34Parser, PythonParserSingle):
if __name__ == '__main__':
# Check grammar
p = Python34Parser()
p.check_grammar()
p.checkGrammar()
from uncompyle6 import PYTHON_VERSION, IS_PYPY
if PYTHON_VERSION == 3.4:
lhs, rhs, tokens, right_recursive = p.check_sets()
lhs, rhs, tokens, right_recursive = p.checkSets()
from uncompyle6.scanner import get_scanner
s = get_scanner(PYTHON_VERSION, IS_PYPY)
opcode_set = set(s.opc.opname).union(set(

View File

@@ -2,7 +2,6 @@
"""
spark grammar differences over Python 3.4 for Python 3.5.
"""
from __future__ import print_function
from uncompyle6.parser import PythonParserSingle
from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
@@ -143,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.kind
opname = token.type
if opname == 'BUILD_MAP_UNPACK_WITH_CALL':
nargs = token.attr % 256
map_unpack_n = "map_unpack_%s" % nargs
@@ -153,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.kind
rule = 'call_function ::= expr unmapexpr ' + call_token.type
self.add_unique_rule(rule, opname, token.attr, customize)
pass
pass
@@ -165,10 +164,10 @@ class Python35ParserSingle(Python35Parser, PythonParserSingle):
if __name__ == '__main__':
# Check grammar
p = Python35Parser()
p.check_grammar()
p.checkGrammar()
from uncompyle6 import PYTHON_VERSION, IS_PYPY
if PYTHON_VERSION == 3.5:
lhs, rhs, tokens, right_recursive = p.check_sets()
lhs, rhs, tokens, right_recursive = p.checkSets()
from uncompyle6.scanner import get_scanner
s = get_scanner(PYTHON_VERSION, IS_PYPY)
opcode_set = set(s.opc.opname).union(set(

View File

@@ -2,7 +2,6 @@
"""
spark grammar differences over Python 3.5 for Python 3.6.
"""
from __future__ import print_function
from uncompyle6.parser import PythonParserSingle
from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
@@ -37,7 +36,7 @@ class Python36Parser(Python35Parser):
def add_custom_rules(self, tokens, customize):
super(Python36Parser, self).add_custom_rules(tokens, customize)
for i, token in enumerate(tokens):
opname = token.kind
opname = token.type
if opname == 'FORMAT_VALUE':
rules_str = """
@@ -65,10 +64,10 @@ class Python36Parser(Python35Parser):
if opname.startswith('CALL_FUNCTION_KW'):
values = 'expr ' * token.attr
rule = 'call_function ::= expr kwargs_only_36 {token.kind}'.format(**locals())
self.add_unique_rule(rule, token.kind, token.attr, customize)
rule = 'call_function ::= expr kwargs_only_36 {token.type}'.format(**locals())
self.add_unique_rule(rule, token.type, token.attr, customize)
rule = 'kwargs_only_36 ::= {values} LOAD_CONST'.format(**locals())
self.add_unique_rule(rule, token.kind, token.attr, customize)
self.add_unique_rule(rule, token.type, token.attr, customize)
else:
super(Python36Parser, self).custom_classfunc_rule(opname, token, customize)
@@ -79,10 +78,10 @@ class Python36ParserSingle(Python36Parser, PythonParserSingle):
if __name__ == '__main__':
# Check grammar
p = Python36Parser()
p.check_grammar()
p.checkGrammar()
from uncompyle6 import PYTHON_VERSION, IS_PYPY
if PYTHON_VERSION == 3.6:
lhs, rhs, tokens, right_recursive = p.check_sets()
lhs, rhs, tokens, right_recursive = p.checkSets()
from uncompyle6.scanner import get_scanner
s = get_scanner(PYTHON_VERSION, IS_PYPY)
opcode_set = set(s.opc.opname).union(set(

View File

@@ -1,41 +0,0 @@
# Copyright (c) 2017 Rocky Bernstein
"""
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
from uncompyle6.parsers.parse36 import Python37Parser
class Python36Parser(Python35Parser):
def __init__(self, debug_parser=PARSER_DEFAULT_DEBUG):
super(Python37Parser, self).__init__(debug_parser)
self.customized = {}
class Python37ParserSingle(Python37Parser, PythonParserSingle):
pass
if __name__ == '__main__':
# Check grammar
p = Python37Parser()
p.check_grammar()
from uncompyle6 import PYTHON_VERSION, IS_PYPY
if PYTHON_VERSION == 3.7:
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(
"""JUMP_BACK CONTINUE RETURN_END_IF COME_FROM
LOAD_GENEXPR LOAD_ASSERT LOAD_SETCOMP LOAD_DICTCOMP LOAD_CLASSNAME
LAMBDA_MARKER RETURN_LAST
""".split()))
remain_tokens = set(tokens) - opcode_set
import re
remain_tokens = set([re.sub('_\d+$', '', t) for t in remain_tokens])
remain_tokens = set([re.sub('_CONT$', '', t) for t in remain_tokens])
remain_tokens = set(remain_tokens) - opcode_set
print(remain_tokens)
# print(sorted(p.rule2name.items()))

View File

@@ -10,19 +10,15 @@ scanner/ingestion module. From here we call various version-specific
scanners, e.g. for Python 2.7 or 3.4.
"""
from __future__ import print_function
import sys
from uncompyle6 import PYTHON3, IS_PYPY
from uncompyle6.scanners.tok import Token
from xdis.bytecode import op_size
from xdis.magics import py_str2float
# 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.7)
3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6)
# FIXME: DRY
if PYTHON3:
@@ -56,7 +52,7 @@ class Scanner(object):
if version in PYTHON_VERSIONS:
if is_pypy:
v_str = "opcode_%spypy" % (int(version * 10))
v_str = "opcode_pypy%s" % (int(version * 10))
else:
v_str = "opcode_%s" % (int(version * 10))
exec("from xdis.opcodes import %s" % v_str)
@@ -65,7 +61,6 @@ 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()
@@ -91,7 +86,7 @@ class Scanner(object):
if op is None:
op = self.code[pos]
target = self.get_argument(pos)
if op in self.opc.JREL_OPS:
if op in self.opc.hasjrel:
target += pos + 3
return target
@@ -102,7 +97,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.opc.hasjabs+self.opc.hasjrel:
dest = self.get_target(i, op)
print('%i\t%s\t%i' % (i, self.opname[op], dest))
else:
@@ -217,6 +212,9 @@ class Scanner(object):
result.append(offset)
return result
def op_hasArgument(self, op):
return self.op_size(op) > 1
def op_range(self, start, end):
"""
Iterate through positions of opcodes, skipping
@@ -224,7 +222,26 @@ class Scanner(object):
"""
while start < end:
yield start
start += op_size(self.code[start], self.opc)
start += self.op_size(self.code[start])
def next_offset(self, op, offset):
return offset + self.op_size(op)
def op_size(self, op):
"""
Return size of operator with its arguments
for given opcode <op>.
"""
if op < self.opc.HAVE_ARGUMENT:
if self.version >= 3.6:
return 2
else:
return 1
else:
if self.version >= 3.6:
return 2
else:
return 3
def remove_mid_line_ifs(self, ifs):
"""
@@ -256,16 +273,13 @@ class Scanner(object):
self.Token = tokenClass
return self.Token
def op_has_argument(op, opc):
return op >= opc.HAVE_ARGUMENT
def parse_fn_counts(argc):
return ((argc & 0xFF), (argc >> 8) & 0xFF, (argc >> 16) & 0x7FFF)
def get_scanner(version, is_pypy=False, show_asm=None):
# If version is a string, turn that into the corresponding float.
if isinstance(version, str):
version = py_str2float(version)
# Pick up appropriate scanner
if version in PYTHON_VERSIONS:
v_str = "%s" % (int(version * 10))
@@ -292,6 +306,5 @@ def get_scanner(version, is_pypy=False, show_asm=None):
if __name__ == "__main__":
import inspect, uncompyle6
co = inspect.currentframe().f_code
scanner = get_scanner('2.7.13', True)
scanner = get_scanner(uncompyle6.PYTHON_VERSION, IS_PYPY, True)
tokens, customize = scanner.ingest(co, {})

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2016-2017 by Rocky Bernstein
# Copyright (c) 2016 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_27pypy
JUMP_OPS = opcode_27pypy.JUMP_OPS
from xdis.opcodes import opcode_pypy27
JUMP_OPs = opcode_pypy27.JUMP_OPs
# We base this off of 2.6 instead of the other way around
# because we cleaned things up this way.

View File

@@ -1,18 +1,22 @@
# Copyright (c) 2017 by Rocky Bernstein
# Copyright (c) 2016 by Rocky Bernstein
"""
Python PyPy 3.2 decompiler scanner.
Python PyPy 3.2 bytecode scanner/deparser
Does some additional massaging of xdis-disassembled instructions to
make things easier for decompilation.
This overlaps Python's 3.2's dis module, but it can be run from
Python 3 and other versions of Python. Also, we save token
information for later use in deparsing.
"""
import uncompyle6.scanners.scanner32 as scan
# bytecode verification, verify(), uses JUMP_OPs from here
from xdis.opcodes import opcode_32 as opc # is this right?
from xdis.opcodes import opcode_32 as opc # is this rgith?
JUMP_OPs = map(lambda op: opc.opname[op], opc.hasjrel + opc.hasjabs)
# We base this off of 3.2
# We base this off of 2.6 instead of the other way around
# because we cleaned things up this way.
# The history is that 2.7 support is the cleanest,
# then from that we got 2.6 and so on.
class ScannerPyPy32(scan.Scanner32):
def __init__(self, show_asm):
# There are no differences in initialization between

View File

@@ -1,22 +0,0 @@
# Copyright (c) 2017 by Rocky Bernstein
"""
Python PyPy 3.2 decompiler scanner.
Does some additional massaging of xdis-disassembled instructions to
make things easier for decompilation.
"""
import uncompyle6.scanners.scanner35 as scan
# bytecode verification, verify(), uses JUMP_OPS from here
from xdis.opcodes import opcode_35 as opc # is this right?
JUMP_OPs = opc.JUMP_OPS
# We base this off of 3.5
class ScannerPyPy35(scan.Scanner35):
def __init__(self, show_asm):
# There are no differences in initialization between
# pypy 3.5 and 3.5
scan.Scanner35.__init__(self, show_asm, is_pypy=True)
self.version = 3.5
return

View File

@@ -1,6 +1,6 @@
# Copyright (c) 2016-2017 by Rocky Bernstein
# Copyright (c) 2016 by Rocky Bernstein
"""
Python 1.5 bytecode decompiler scanner.
Python 1.5 bytecode scanner/deparser
This massages tokenized 1.5 bytecode to make it more amenable for
grammar parsing.
@@ -11,15 +11,15 @@ 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.2 instead of the other way around
# We base this off of 2.1 instead of the other way around
# because we cleaned things up this way.
# The history is that 2.7 support is the cleanest,
# then from that we got 2.6 and so on.
class Scanner15(scan.Scanner21):
def __init__(self, show_asm=False):
scan.Scanner21.__init__(self, show_asm)
scan.Scanner21.__init__(self, show_asm=False)
self.opc = opcode_15
self.opname = opcode_15.opname
self.version = 1.5

View File

@@ -20,14 +20,17 @@ For example:
Finally we save token information.
"""
from __future__ import print_function
from uncompyle6 import PYTHON_VERSION
if PYTHON_VERSION < 2.6:
from xdis.namedtuple24 import namedtuple
else:
from collections import namedtuple
from collections import namedtuple
from array import array
from uncompyle6.scanner import L65536
from uncompyle6.scanner import op_has_argument, L65536
from xdis.code import iscode
from xdis.bytecode import op_has_argument, op_size
from uncompyle6.scanner import Scanner
@@ -85,7 +88,9 @@ class Scanner2(Scanner):
cause specific rules for the specific number of arguments they take.
"""
show_asm = self.show_asm if not show_asm else show_asm
if not show_asm:
show_asm = self.show_asm
# show_asm = 'after'
if show_asm in ('both', 'before'):
from xdis.bytecode import Bytecode
@@ -93,18 +98,12 @@ class Scanner2(Scanner):
for instr in bytecode.get_instructions(co):
print(instr._disassemble())
# list of tokens/instructions
# Container for tokens
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'] = 0
customize['PyPy'] = 1
Token = self.Token # shortcut
@@ -195,7 +194,7 @@ class Scanner2(Scanner):
if op == self.opc.EXTENDED_ARG:
extended_arg = oparg * L65536
continue
if op in self.opc.CONST_OPS:
if op in self.opc.hasconst:
const = co.co_consts[oparg]
if iscode(const):
oparg = const
@@ -216,23 +215,23 @@ class Scanner2(Scanner):
pattr = '<code_object ' + const.co_name + '>'
else:
pattr = const
elif op in self.opc.NAME_OPS:
elif op in self.opc.hasname:
pattr = names[oparg]
elif op in self.opc.JREL_OPS:
elif op in self.opc.hasjrel:
# use instead: hasattr(self, 'patch_continue'): ?
if self.version == 2.7:
self.patch_continue(tokens, offset, op)
pattr = repr(offset + 3 + oparg)
elif op in self.opc.JABS_OPS:
elif op in self.opc.hasjabs:
# use instead: hasattr(self, 'patch_continue'): ?
if self.version == 2.7:
self.patch_continue(tokens, offset, op)
pattr = repr(oparg)
elif op in self.opc.LOCAL_OPS:
elif op in self.opc.haslocal:
pattr = varnames[oparg]
elif op in self.opc.COMPARE_OPS:
elif op in self.opc.hascompare:
pattr = self.opc.cmp_op[oparg]
elif op in self.opc.FREE_OPS:
elif op in self.opc.hasfree:
pattr = free[oparg]
if op in self.varargs_ops:
@@ -334,7 +333,7 @@ class Scanner2(Scanner):
for i in self.op_range(0, n):
op = self.code[i]
self.prev.append(i)
if op_has_argument(op, self.opc):
if self.op_hasArgument(op):
self.prev.append(i)
self.prev.append(i)
pass
@@ -387,7 +386,7 @@ class Scanner2(Scanner):
if elem != code[i]:
match = False
break
i += op_size(code[i], self.opc)
i += self.op_size(code[i])
if match:
i = self.prev[i]
@@ -458,7 +457,7 @@ class Scanner2(Scanner):
self.not_continue.add(jmp)
jmp = self.get_target(jmp)
prev_offset = self.prev[except_match]
# COMPARE_OP argument should be "exception-match" or 10
# COMPARE_OP argument should be "exception match" or 10
if (self.code[prev_offset] == self.opc.COMPARE_OP and
self.code[prev_offset+1] != 10):
return None
@@ -609,7 +608,7 @@ class Scanner2(Scanner):
if test == offset:
loop_type = 'while 1'
elif self.code[test] in self.opc.JUMP_OPs:
elif self.code[test] in self.opc.hasjabs + self.opc.hasjrel:
self.ignore_if.add(test)
test_target = self.get_target(test)
if test_target > (jump_back+3):
@@ -624,7 +623,7 @@ class Scanner2(Scanner):
'start': jump_back+3,
'end': end})
elif op == self.opc.SETUP_EXCEPT:
start = offset + op_size(op, self.opc)
start = offset + self.op_size(op)
target = self.get_target(offset, op)
end = self.restrict_to_parent(target, parent)
if target != end:
@@ -648,7 +647,7 @@ class Scanner2(Scanner):
setup_except_nest -= 1
elif self.code[end_finally_offset] == self.opc.SETUP_EXCEPT:
setup_except_nest += 1
end_finally_offset += op_size(code[end_finally_offset], self.opc)
end_finally_offset += self.op_size(code[end_finally_offset])
pass
# Add the except blocks
@@ -849,7 +848,7 @@ class Scanner2(Scanner):
else:
# We still have the case in 2.7 that the next instruction
# is a jump to a SETUP_LOOP target.
next_offset = target + op_size(self.code[target], self.opc)
next_offset = target + self.op_size(self.code[target])
next_op = self.code[next_offset]
if self.op_name(next_op) == 'JUMP_FORWARD':
jump_target = self.get_target(next_offset, next_op)
@@ -911,9 +910,7 @@ class Scanner2(Scanner):
'start': start-3,
'end': pre_rtarget})
# FIXME: this is yet another case were we need dominators.
if pre_rtarget not in self.linestartoffsets or self.version < 2.7:
self.not_continue.add(pre_rtarget)
self.not_continue.add(pre_rtarget)
if rtarget < end:
# We have an "else" block of some kind.
@@ -1000,11 +997,11 @@ class Scanner2(Scanner):
oparg = self.get_argument(offset)
if label is None:
if op in self.opc.JREL_OPS and self.op_name(op) != 'FOR_ITER':
# if (op in self.opc.JREL_OPS and
if op in self.opc.hasjrel and self.op_name(op) != 'FOR_ITER':
# if (op in self.opc.hasjrel and
# (self.version < 2.0 or op != self.opc.FOR_ITER)):
label = offset + 3 + oparg
elif self.version == 2.7 and op in self.opc.JABS_OPS:
elif self.version == 2.7 and op in self.opc.hasjabs:
if op in (self.opc.JUMP_IF_FALSE_OR_POP,
self.opc.JUMP_IF_TRUE_OR_POP):
if (oparg > offset):
@@ -1032,8 +1029,10 @@ class Scanner2(Scanner):
# FIXME: rocky: I think we need something like this...
if offset not in set(self.ignore_if) or self.version == 2.7:
source = (self.setup_loops[label]
if label in self.setup_loops else offset)
if label in self.setup_loops:
source = self.setup_loops[label]
else:
source = offset
targets[label] = targets.get(label, []) + [source]
pass

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2016-2017 by Rocky Bernstein
# Copyright (c) 2016 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.
@@ -19,7 +19,7 @@ JUMP_OPS = opcode_21.JUMP_OPS
# then from that we got 2.6 and so on.
class Scanner21(scan.Scanner22):
def __init__(self, show_asm=False):
scan.Scanner22.__init__(self, show_asm)
scan.Scanner22.__init__(self, show_asm=False)
self.opc = opcode_21
self.opname = opcode_21.opname
self.version = 2.1

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2016-2017 by Rocky Bernstein
# Copyright (c) 2016 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.
@@ -19,7 +19,7 @@ JUMP_OPS = opcode_22.JUMP_OPS
# then from that we got 2.6 and so on.
class Scanner22(scan.Scanner23):
def __init__(self, show_asm=False):
scan.Scanner23.__init__(self, show_asm)
scan.Scanner23.__init__(self, show_asm=False)
self.opc = opcode_22
self.opname = opcode_22.opname
self.version = 2.2
@@ -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.kind != 'SET_LINENO']
tokens = [t for t in tokens if t.type != 'SET_LINENO']
return tokens, customize

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2016-2017 by Rocky Bernstein
# Copyright (c) 2016 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.

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2016-2017 by Rocky Bernstein
# Copyright (c) 2016 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.

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2015-2017 by Rocky Bernstein
# Copyright (c) 2015-2016 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.

View File

@@ -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):
@@ -87,7 +87,9 @@ class Scanner26(scan.Scanner2):
cause specific rules for the specific number of arguments they take.
"""
show_asm = self.show_asm if not show_asm else show_asm
if not show_asm:
show_asm = self.show_asm
# show_asm = 'after'
if show_asm in ('both', 'before'):
from xdis.bytecode import Bytecode
@@ -181,7 +183,7 @@ class Scanner26(scan.Scanner2):
if op == self.opc.EXTENDED_ARG:
extended_arg = oparg * L65536
continue
if op in self.opc.CONST_OPS:
if op in self.opc.hasconst:
const = co.co_consts[oparg]
# We can't use inspect.iscode() because we may be
# using a different version of Python than the
@@ -206,25 +208,25 @@ class Scanner26(scan.Scanner2):
pattr = '<code_object ' + const.co_name + '>'
else:
pattr = const
elif op in self.opc.NAME_OPS:
elif op in self.opc.hasname:
pattr = names[oparg]
elif op in self.opc.JREL_OPS:
elif op in self.opc.hasjrel:
pattr = repr(offset + 3 + oparg)
if op == self.opc.JUMP_FORWARD:
target = self.get_target(offset)
# 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].kind == 'JUMP_BACK':
tokens[-1].kind = intern('CONTINUE')
if len(tokens) and tokens[-1].type == 'JUMP_BACK':
tokens[-1].type = intern('CONTINUE')
elif op in self.opc.JABS_OPS:
elif op in self.opc.hasjabs:
pattr = repr(oparg)
elif op in self.opc.LOCAL_OPS:
elif op in self.opc.haslocal:
pattr = varnames[oparg]
elif op in self.opc.COMPARE_OPS:
elif op in self.opc.hascompare:
pattr = self.opc.cmp_op[oparg]
elif op in self.opc.FREE_OPS:
elif op in self.opc.hasfree:
pattr = free[oparg]
if op in self.varargs_ops:
# CE - Hack for >= 2.5
@@ -256,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].kind == 'JUMP_BACK')
tokens[-1].type == '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].kind == 'JUMP_BACK':
if tokens[-1].type == 'JUMP_BACK':
# We need 'intern' since we have
# already have processed the previous
# token.
tokens[-1].kind = intern('CONTINUE')
tokens[-1].type = intern('CONTINUE')
elif op == self.opc.LOAD_GLOBAL:
if offset in self.load_asserts:

View File

@@ -7,8 +7,6 @@ grammar parsing.
"""
from __future__ import print_function
from uncompyle6.scanners.scanner2 import Scanner2
from uncompyle6 import PYTHON3
@@ -18,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):
@@ -94,9 +92,9 @@ class Scanner27(Scanner2):
# the "continue" is not on a new line.
n = len(tokens)
if (n > 2 and
tokens[-1].kind == 'JUMP_BACK' and
tokens[-1].type == 'JUMP_BACK' and
self.code[offset+3] == self.opc.END_FINALLY):
tokens[-1].kind = intern('CONTINUE')
tokens[-1].type = intern('CONTINUE')
pass

View File

@@ -20,16 +20,19 @@ For example:
Finally we save token information.
"""
from __future__ import print_function
from uncompyle6 import PYTHON_VERSION
if PYTHON_VERSION < 2.6:
from xdis.namedtuple24 import namedtuple
else:
from collections import namedtuple
from collections import namedtuple
from array import array
from uncompyle6.scanner import Scanner
from uncompyle6.scanner import Scanner, op_has_argument
from xdis.code import iscode
from xdis.bytecode import Bytecode, op_has_argument, op_size
from xdis.bytecode import Bytecode
from uncompyle6.scanner import Token, parse_fn_counts
import xdis
# Get all the opcodes into globals
import xdis.opcodes.opcode_33 as op3
@@ -162,23 +165,21 @@ class Scanner3(Scanner):
cause specific rules for the specific number of arguments they take.
"""
show_asm = self.show_asm if not show_asm else show_asm
# show_asm = 'both'
if not show_asm:
show_asm = self.show_asm
# show_asm = 'after'
if show_asm in ('both', 'before'):
bytecode = Bytecode(co, self.opc)
for instr in bytecode.get_instructions(co):
print(instr._disassemble())
# list of tokens/instructions
# Container for tokens
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 = {}
if self.is_pypy:
customize['PyPy'] = 0
customize['PyPy'] = 1
self.code = array('B', co.co_code)
self.build_lines_data(co)
@@ -334,7 +335,7 @@ class Scanner3(Scanner):
attr = (pos_args, name_pair_args, annotate_args)
tokens.append(
Token(
opname = opname,
type_ = opname,
attr = attr,
pattr = pattr,
offset = inst.offset,
@@ -394,12 +395,12 @@ 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].kind == 'JUMP_BACK' and tokens[-1].attr <= argval:
if tokens[-2].kind == 'BREAK_LOOP':
if tokens[-1].type == 'JUMP_BACK' and tokens[-1].attr <= argval:
if tokens[-2].type == 'BREAK_LOOP':
del tokens[-1]
else:
# intern is used because we are changing the *previous* token
tokens[-1].kind = intern('CONTINUE')
tokens[-1].type = intern('CONTINUE')
if last_op_was_break and opname == 'CONTINUE':
last_op_was_break = False
continue
@@ -412,7 +413,7 @@ class Scanner3(Scanner):
last_op_was_break = opname == 'BREAK_LOOP'
tokens.append(
Token(
opname = opname,
type_ = opname,
attr = argval,
pattr = pattr,
offset = inst.offset,
@@ -473,7 +474,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(self.op_size(op)):
self.prev_op.append(offset)
def find_jump_targets(self, debug):
@@ -523,7 +524,7 @@ class Scanner3(Scanner):
oparg = code[offset+1]
else:
oparg = code[offset+1] + code[offset+2] * 256
next_offset = xdis.next_offset(op, self.opc, offset)
next_offset = self.next_offset(op, offset)
if label is None:
if op in op3.hasjrel and op != self.opc.FOR_ITER:
@@ -569,7 +570,7 @@ class Scanner3(Scanner):
if elem != code[i]:
match = False
break
i += op_size(code[i], self.opc)
i += self.op_size(code[i])
if match is True:
i = self.prev_op[i]
@@ -637,11 +638,11 @@ class Scanner3(Scanner):
rel_offset = 0
if self.version >= 3.6:
target = self.code[offset+1]
if op in self.opc.JREL_OPS:
if op in self.opc.hasjrel:
rel_offset = offset + 2
else:
target = self.code[offset+1] + self.code[offset+2] * 256
if op in self.opc.JREL_OPS:
if op in self.opc.hasjrel:
rel_offset = offset + 3
pass
pass
@@ -762,7 +763,7 @@ class Scanner3(Scanner):
'start': jump_back+3,
'end': end})
elif op in self.pop_jump_tf:
start = offset + op_size(op, self.opc)
start = offset + self.op_size(op)
target = self.get_target(offset)
rtarget = self.restrict_to_parent(target, parent)
prev_op = self.prev_op
@@ -779,10 +780,6 @@ 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,
@@ -929,7 +926,7 @@ class Scanner3(Scanner):
# except block return
jump_prev = prev_op[offset]
if self.is_pypy and code[jump_prev] == self.opc.COMPARE_OP:
if self.opc.cmp_op[code[jump_prev+1]] == 'exception-match':
if self.opc.cmp_op[code[jump_prev+1]] == 'exception match':
return
if self.version >= 3.5:
# Python 3.5 may remove as dead code a JUMP
@@ -941,9 +938,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 += self.op_size(self.code[next_op])
if code[next_op] == self.opc.JUMP_ABSOLUTE:
next_op += op_size(self.code[next_op], self.opc)
next_op += self.op_size(self.code[next_op])
if next_op in targets:
for try_op in targets[next_op]:
come_from_op = code[try_op]
@@ -951,7 +948,7 @@ class Scanner3(Scanner):
return
pass
pass
if code[pre_rtarget] == self.opc.RETURN_VALUE:
if code[pre_rtarget] == self.opc.RETURN_VALUE and self.version < 3.5:
self.return_end_ifs.add(pre_rtarget)
else:
self.fixed_jumps[offset] = rtarget
@@ -966,12 +963,12 @@ class Scanner3(Scanner):
end = self.restrict_to_parent(target, parent)
self.fixed_jumps[offset] = end
elif op == self.opc.POP_EXCEPT:
next_offset = xdis.next_offset(op, self.opc, offset)
next_offset = self.next_offset(op, offset)
target = self.get_target(next_offset)
if target > next_offset:
next_op = code[next_offset]
if (self.opc.JUMP_ABSOLUTE == next_op and
self.opc.END_FINALLY != code[xdis.next_offset(next_op, self.opc, next_offset)]):
END_FINALLY != code[self.next_offset(next_op, next_offset)]):
self.fixed_jumps[next_offset] = target
self.except_targets[target] = next_offset
@@ -994,8 +991,7 @@ 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:
next_offset = xdis.next_offset(op, self.opc, offset)
if (next_offset < len(code) and code[next_offset] == self.opc.JUMP_ABSOLUTE and
if (offset+1 < len(code) and code[offset+1] == self.opc.JUMP_ABSOLUTE and
offset in self.return_end_ifs):
self.return_end_ifs.remove(offset)
pass

View File

@@ -6,13 +6,9 @@ This sets up opcodes Python's 3.0 and calls a generalized
scanner routine for Python 3.
"""
from __future__ import print_function
# bytecode verification, verify(), uses JUMP_OPs from here
from xdis.opcodes import opcode_30 as opc
from xdis.bytecode import op_size
JUMP_OPS = opc.JUMP_OPS
JUMP_OPs = map(lambda op: opc.opname[op], opc.hasjrel + opc.hasjabs)
JUMP_TF = frozenset([opc.JUMP_IF_FALSE, opc.JUMP_IF_TRUE])
@@ -120,7 +116,7 @@ class Scanner30(Scanner3):
if test == offset:
loop_type = 'while 1'
elif self.code[test] in opc.JUMP_OPs:
elif self.code[test] in opc.hasjabs+opc.hasjrel:
self.ignore_if.add(test)
test_target = self.get_target(test)
if test_target > (jump_back+3):
@@ -135,7 +131,7 @@ class Scanner30(Scanner3):
'start': jump_back+3,
'end': end})
elif op in JUMP_TF:
start = offset + op_size(op, self.opc)
start = offset + self.op_size(op)
target = self.get_target(offset)
rtarget = self.restrict_to_parent(target, parent)
prev_op = self.prev_op
@@ -295,7 +291,7 @@ class Scanner30(Scanner3):
# except block return
jump_prev = prev_op[offset]
if self.is_pypy and code[jump_prev] == self.opc.COMPARE_OP:
if self.opc.cmp_op[code[jump_prev+1]] == 'exception-match':
if self.opc.cmp_op[code[jump_prev+1]] == 'exception match':
return
if self.version >= 3.5:
# Python 3.5 may remove as dead code a JUMP
@@ -307,9 +303,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 += self.op_size(self.code[next_op])
if code[next_op] == self.opc.JUMP_ABSOLUTE:
next_op += op_size(self.code[next_op], self.opc)
next_op += self.op_size(self.code[next_op])
if next_op in targets:
for try_op in targets[next_op]:
come_from_op = code[try_op]

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2016-2017 by Rocky Bernstein
# Copyright (c) 2016 by Rocky Bernstein
"""
Python 3.1 bytecode scanner/deparser
@@ -6,11 +6,9 @@ This sets up opcodes Python's 3.1 and calls a generalized
scanner routine for Python 3.
"""
from __future__ import print_function
# bytecode verification, verify(), uses JUMP_OPs from here
from xdis.opcodes import opcode_31 as opc
JUMP_OPS = opc.JUMP_OPS
JUMP_OPs = map(lambda op: opc.opname[op], opc.hasjrel + opc.hasjabs)
from uncompyle6.scanners.scanner3 import Scanner3
class Scanner31(Scanner3):

View File

@@ -1,19 +1,14 @@
# Copyright (c) 2015-2017 by Rocky Bernstein
# Copyright (c) 2015-2016 by Rocky Bernstein
"""
Python 3.2 bytecode decompiler scanner.
Does some additional massaging of xdis-disassembled instructions to
make things easier for decompilation.
Python 3.2 bytecode scanner/deparser
This sets up opcodes Python's 3.2 and calls a generalized
scanner routine for Python 3.
"""
from __future__ import print_function
# bytecode verification, verify(), uses JUMP_OPs from here
from xdis.opcodes import opcode_32 as opc
JUMP_OPS = opc.JUMP_OPS
JUMP_OPs = map(lambda op: opc.opname[op], opc.hasjrel + opc.hasjabs)
from uncompyle6.scanners.scanner3 import Scanner3
class Scanner32(Scanner3):

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2015-2017 by Rocky Bernstein
# Copyright (c) 2015-2016 by Rocky Bernstein
"""
Python 3.3 bytecode scanner/deparser
@@ -6,11 +6,9 @@ This sets up opcodes Python's 3.3 and calls a generalized
scanner routine for Python 3.
"""
from __future__ import print_function
# bytecode verification, verify(), uses JUMP_OPs from here
from xdis.opcodes import opcode_33 as opc
JUMP_OPS = opc.JUMP_OPS
JUMP_OPs = map(lambda op: opc.opname[op], opc.hasjrel + opc.hasjabs)
from uncompyle6.scanners.scanner3 import Scanner3
class Scanner33(Scanner3):

View File

@@ -1,20 +1,15 @@
# Copyright (c) 2015-2017 by Rocky Bernstein
# Copyright (c) 2015-2016 by Rocky Bernstein
"""
Python 3.4 bytecode decompiler scanner
Does some additional massaging of xdis-disassembled instructions to
make things easier for decompilation.
Python 3.4 bytecode scanner/deparser
This sets up opcodes Python's 3.4 and calls a generalized
scanner routine for Python 3.
"""
from __future__ import print_function
from xdis.opcodes import opcode_34 as opc
# bytecode verification, verify(), uses JUMP_OPs from here
JUMP_OPS = opc.JUMP_OPS
JUMP_OPs = map(lambda op: opc.opname[op], opc.hasjrel + opc.hasjabs)
from uncompyle6.scanners.scanner3 import Scanner3

View File

@@ -1,26 +1,21 @@
# Copyright (c) 2017 by Rocky Bernstein
# Copyright (c) 2016 by Rocky Bernstein
"""
Python 3.5 bytecode decompiler scanner
Does some additional massaging of xdis-disassembled instructions to
make things easier for decompilation.
Python 3.5 bytecode scanner/deparser
This sets up opcodes Python's 3.5 and calls a generalized
scanner routine for Python 3.
"""
from __future__ import print_function
from uncompyle6.scanners.scanner3 import Scanner3
# bytecode verification, verify(), uses JUMP_OPs from here
from xdis.opcodes import opcode_35 as opc
JUMP_OPS = opc.JUMP_OPS
JUMP_OPs = map(lambda op: opc.opname[op], opc.hasjrel + opc.hasjabs)
class Scanner35(Scanner3):
def __init__(self, show_asm=None, is_pypy=False):
Scanner3.__init__(self, 3.5, show_asm, is_pypy)
def __init__(self, show_asm=None):
Scanner3.__init__(self, 3.5, show_asm)
return
pass

View File

@@ -1,21 +1,16 @@
# Copyright (c) 2016-2017 by Rocky Bernstein
"""
Python 3.6 bytecode decompiler scanner
Does some additional massaging of xdis-disassembled instructions to
make things easier for decompilation.
Python 3.6 bytecode scanner/deparser
This sets up opcodes Python's 3.6 and calls a generalized
scanner routine for Python 3.
"""
from __future__ import print_function
from uncompyle6.scanners.scanner3 import Scanner3
# bytecode verification, verify(), uses JUMP_OPS from here
# bytecode verification, verify(), uses JUMP_OPs from here
from xdis.opcodes import opcode_36 as opc
JUMP_OPS = opc.JUMP_OPS
JUMP_OPs = map(lambda op: opc.opname[op], opc.hasjrel + opc.hasjabs)
class Scanner36(Scanner3):

View File

@@ -1,38 +0,0 @@
# 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 __future__ import print_function
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)

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2016-2017 by Rocky Bernstein
# Copyright (c) 2016 by Rocky Bernstein
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
# Copyright (c) 1999 John Aycock
@@ -8,7 +8,7 @@ from uncompyle6 import PYTHON3
if PYTHON3:
intern = sys.intern
class Token():
class Token:
"""
Class representing a byte-code instruction.
@@ -16,12 +16,13 @@ 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, opname, attr=None, pattr=None, offset=-1,
def __init__(self, type_, attr=None, pattr=None, offset=-1,
linestart=None, op=None, has_arg=None, opc=None):
self.kind = intern(opname)
self.type = intern(type_)
self.op = op
self.has_arg = has_arg
self.attr = attr
@@ -38,36 +39,42 @@ class Token():
if isinstance(o, Token):
# Both are tokens: compare type and attr
# It's okay if offsets are different
return (self.kind == o.kind) and (self.pattr == o.pattr)
return (self.type == o.type) and (self.pattr == o.pattr)
else:
return self.kind == o
return self.type == o
def __repr__(self):
return str(self.kind)
return str(self.type)
# 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.kind, pattr)))
# ('%9s %-18s %r' % (self.offset, self.type, pattr)))
def __str__(self):
return self.format(line_prefix='')
def format(self, line_prefix=''):
prefix = ('\n%s%4d ' % (line_prefix, self.linestart)
if self.linestart else (' ' * (6 + len(line_prefix))))
offset_opname = '%6s %-17s' % (self.offset, self.kind)
if self.linestart:
prefix = '\n%s%4d ' % (line_prefix, self.linestart)
else:
prefix = ' ' * (6 + len(line_prefix))
offset_opname = '%6s %-17s' % (self.offset, self.type)
if not self.has_arg:
return "%s%s" % (prefix, offset_opname)
argstr = "%6d " % self.attr if isinstance(self.attr, int) else (' '*7)
if isinstance(self.attr, int):
argstr = "%6d " % self.attr
else:
argstr = ' '*7
if self.pattr:
pattr = self.pattr
if self.opc:
if self.op in self.opc.JREL_OPS:
if self.op in self.opc.hasjrel:
if not self.pattr.startswith('to '):
pattr = "to " + self.pattr
elif self.op in self.opc.JABS_OPS:
elif self.op in self.opc.hasjabs:
self.pattr= str(self.pattr)
if not self.pattr.startswith('to '):
pattr = "to " + str(self.pattr)
@@ -77,14 +84,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.kind):
elif re.search('_\d+$', self.type):
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.kind)
return hash(self.type)
def __getitem__(self, i):
raise IndexError

View File

@@ -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.kind in ('while1stmt', 'whileTruestmt',
in_loop = in_loop or ast.type in ('while1stmt', 'whileTruestmt',
'whilestmt', 'whileelsestmt', 'while1elsestmt',
'for_block')
if ast.kind in ('augassign1', 'augassign2') and ast[0][0] == 'and':
if ast.type 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.kind in ('continue_stmt', 'break_stmt'):
if not in_loop and node.type 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)

View File

@@ -1,5 +1,5 @@
# Copyright (c) 2017 by Rocky Bernstein
"""Constants and initial table values used in pysource.py and fragments.py"""
"""Constants used in pysource.py"""
import re, sys
from uncompyle6.parsers.astnode import AST
@@ -57,7 +57,9 @@ 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 = {
@@ -65,9 +67,8 @@ TABLE_R0 = {
# 'BUILD_TUPLE': ( '(%C)', (0,-1,', ') ),
# 'CALL_FUNCTION': ( '%c(%P)', 0, (1,-1,', ') ),
}
TABLE_DIRECT = {
'BINARY_ADD': ( '+' ,),
'BINARY_ADD': ( '+' ,),
'BINARY_SUBTRACT': ( '-' ,),
'BINARY_MULTIPLY': ( '*' ,),
'BINARY_DIVIDE': ( '/' ,),
@@ -75,13 +76,13 @@ TABLE_DIRECT = {
'BINARY_TRUE_DIVIDE': ( '/' ,), # Not in <= 2.1
'BINARY_FLOOR_DIVIDE': ( '//' ,),
'BINARY_MODULO': ( '%%',),
'BINARY_POWER': ( '**',),
'BINARY_POWER': ( '**',),
'BINARY_LSHIFT': ( '<<',),
'BINARY_RSHIFT': ( '>>',),
'BINARY_AND': ( '&' ,),
'BINARY_OR': ( '|' ,),
'BINARY_XOR': ( '^' ,),
'INPLACE_ADD': ( '+=' ,),
'BINARY_AND': ( '&' ,),
'BINARY_OR': ( '|' ,),
'BINARY_XOR': ( '^' ,),
'INPLACE_ADD': ( '+=' ,),
'INPLACE_SUBTRACT': ( '-=' ,),
'INPLACE_MULTIPLY': ( '*=' ,),
'INPLACE_MATRIX_MULTIPLY': ( '@=' ,),
@@ -92,126 +93,125 @@ TABLE_DIRECT = {
'INPLACE_POWER': ( '**=',),
'INPLACE_LSHIFT': ( '<<=',),
'INPLACE_RSHIFT': ( '>>=',),
'INPLACE_AND': ( '&=' ,),
'INPLACE_OR': ( '|=' ,),
'INPLACE_XOR': ( '^=' ,),
'binary_expr': ( '%c %c %c', 0, -1, 1 ),
'INPLACE_AND': ( '&=' ,),
'INPLACE_OR': ( '|=' ,),
'INPLACE_XOR': ( '^=' ,),
'binary_expr': ( '%c %c %c', 0, -1, 1 ),
'UNARY_POSITIVE': ( '+',),
'UNARY_NEGATIVE': ( '-',),
'UNARY_INVERT': ( '~'),
'unary_expr': ( '%c%c', 1, 0),
'UNARY_INVERT': ( '~%c'),
'unary_expr': ( '%c%c', 1, 0),
'unary_not': ( 'not %c', 0 ),
'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) ),
'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) ),
'IMPORT_FROM': ( '%{pattr}', ),
'load_attr': ( '%c.%[1]{pattr}', 0),
'LOAD_FAST': ( '%{pattr}', ),
'LOAD_NAME': ( '%{pattr}', ),
'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_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_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, ', ') ),
'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, 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)),
'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} %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 +229,37 @@ 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),
'importfrom': ( '%|from %[2]{pattr} import %c\n', 3 ),
'importstar': ( '%|from %[2]{pattr} import *\n', ),
}
@@ -276,7 +276,7 @@ MAP = {
}
# Operator precidence
# See https://docs.python.org/2/reference/expressions.html
# See https://docs.python.org/3/reference/expressions.html
# or https://docs.python.org/3/reference/expressions.html
# for a list.
PRECEDENCE = {
@@ -332,7 +332,6 @@ PRECEDENCE = {
'ret_or': 26,
'conditional': 28,
'conditional_lamdba': 28,
'conditionalnot': 28,
'ret_cond': 28,
'ret_cond_not': 28,

View File

@@ -1,4 +1,6 @@
# Copyright (c) 2015-2017 by Rocky Bernstein
# 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) 1999 John Aycock
"""
@@ -6,8 +8,8 @@ Creates Python source code from an uncompyle6 abstract syntax tree,
and indexes fragments which can be accessed by instruction offset
address.
See https://github.com/rocky/python-uncompyle6/wiki/Table-driven-semantic-actions.
for a more complete explanation, nicely marked up and with examples.
See the comments in pysource for information on the abstract sytax tree
and how semantic actions are written.
We add some format specifiers here not used in pysource
@@ -38,8 +40,7 @@ do it recursively which is where offsets are probably located.
2. %b
-----
%b associates the text from the specified index to what we have now.
it takes an integer argument.
%b associates the text from the previous start node up to what we have now
For example in:
'importmultiple': ( '%|import%b %c%c\n', 0, 2, 3 ),
@@ -50,8 +51,6 @@ The node position 0 will be associated with "import".
# FIXME: DRY code with pysource
from __future__ import print_function
import re, sys
from xdis.code import iscode
@@ -78,7 +77,12 @@ from uncompyle6.semantics.consts import (
from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
from spark_parser.ast import GenericASTTraversalPruningException
from collections import namedtuple
from uncompyle6 import PYTHON_VERSION
if PYTHON_VERSION < 2.6:
from xdis.namedtuple24 import namedtuple
else:
from collections import namedtuple
NodeInfo = namedtuple("NodeInfo", "node start finish")
ExtractInfo = namedtuple("ExtractInfo",
"lineNo lineStartOffset markerLine selectedLine selectedText nonterminal")
@@ -92,8 +96,9 @@ 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%x in %c:\n%+%c%-%|else:\n%+%c%-\n\n', 3, (3, (2,)), 1, 4, -2),
'%|for %c in %c%x:\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': (
@@ -305,11 +310,11 @@ class FragmentsWalker(pysource.SourceWalker, object):
def n_expr(self, node):
start = len(self.f.getvalue())
p = self.prec
if node[0].kind.startswith('binary_expr'):
if node[0].type.startswith('binary_expr'):
n = node[0][-1][0]
else:
n = node[0]
self.prec = PRECEDENCE.get(n.kind, -2)
self.prec = PRECEDENCE.get(n.type, -2)
if n == 'LOAD_CONST' and repr(n.pattr)[0] == '-':
n.parent = node
self.set_pos_info(n, start, len(self.f.getvalue()))
@@ -402,7 +407,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
def n_ifelsestmtr(self, node):
if node[2] == 'COME_FROM':
return_stmts_node = node[3]
node.kind = 'ifelsestmtr2'
node.type = 'ifelsestmtr2'
else:
return_stmts_node = node[2]
if len(return_stmts_node) != 2:
@@ -419,10 +424,10 @@ class FragmentsWalker(pysource.SourceWalker, object):
self.write(self.indent, 'if ')
self.preorder(node[0])
self.println(':')
self.indent_more()
self.indentMore()
node[1].parent = node
self.preorder(node[1])
self.indent_less()
self.indentLess()
if_ret_at_end = False
if len(node[2][0]) >= 3:
@@ -435,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].kind = 'elifstmt'
n[0].type = '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.indent_more()
self.indentMore()
past_else = True
n.parent = node
self.preorder(n)
if not past_else or if_ret_at_end:
self.println(self.indent, 'else:')
self.indent_more()
self.indentMore()
node[2][1].parent = node
self.preorder(node[2][1])
self.set_pos_info(node, start, len(self.f.getvalue()))
self.indent_less()
self.indentLess()
self.prune()
def n_elifelsestmtr(self, node):
@@ -468,20 +473,20 @@ class FragmentsWalker(pysource.SourceWalker, object):
node[0].parent = node
self.preorder(node[0])
self.println(':')
self.indent_more()
self.indentMore()
node[1].parent = node
self.preorder(node[1])
self.indent_less()
self.indentLess()
for n in node[2][0]:
n[0].kind = 'elifstmt'
n[0].type = 'elifstmt'
n.parent = node
self.preorder(n)
self.println(self.indent, 'else:')
self.indent_more()
self.indentMore()
node[2][1].parent = node
self.preorder(node[2][1])
self.indent_less()
self.indentLess()
self.set_pos_info(node, start, len(self.f.getvalue()))
self.prune()
@@ -490,7 +495,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
iname = node[0].pattr
store_import_node = node[-1][-1]
assert store_import_node.kind.startswith('STORE_')
assert store_import_node.type.startswith('STORE_')
sname = store_import_node.pattr
self.write(iname)
@@ -525,7 +530,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
self.write(func_name)
self.set_pos_info(code_node, start, len(self.f.getvalue()))
self.indent_more()
self.indentMore()
start = len(self.f.getvalue())
self.make_function(node, isLambda=False, codeNode=code_node)
@@ -535,7 +540,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
self.write('\n\n')
else:
self.write('\n\n\n')
self.indent_less()
self.indentLess()
self.prune() # stop recursing
def n_list_compr(self, node):
@@ -551,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].kind.startswith('BUILD_LIST'):
if node[0].type.startswith('BUILD_LIST'):
start = len(self.f.getvalue())
self.set_pos_info(node[0], start, start+1)
self.write( '[ ')
@@ -684,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.kind in ('lc_body', 'comp_body', 'setcomp_func', 'set_comp_body'), ast
assert n.type 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
@@ -711,7 +716,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
self.preorder(if_node)
self.prec = p
self.name = old_name
if node[-1].kind.startswith('CALL_FUNCTION'):
if node[-1].type.startswith('CALL_FUNCTION'):
self.set_pos_info(node[-1], gen_start, len(self.f.getvalue()))
def listcomprehension_walk2(self, node):
@@ -740,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].kind == 'expr':
if n[0].type == 'expr':
list_if = n
else:
list_if = n[1]
@@ -770,7 +775,10 @@ class FragmentsWalker(pysource.SourceWalker, object):
def n_genexpr(self, node):
start = len(self.f.getvalue())
self.write('(')
code_index = -6 if self.version > 3.2 else -5
if self.version > 3.2:
code_index = -6
else:
code_index = -5
self.comprehension_walk(node, iter_index=3, code_index=code_index)
self.write(')')
self.set_pos_info(node, start, len(self.f.getvalue()))
@@ -783,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].kind == 'load_closure':
elif node[0].type == 'load_closure':
self.setcomprehension_walk3(node, collection_index=4)
else:
self.comprehension_walk(node, iter_index=4)
@@ -802,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].kind.startswith('BUILD_SET')
assert node[0].type.startswith('BUILD_SET')
self.set_pos_info(node[0], start-1, start)
designator = node[3]
assert designator == 'designator'
@@ -811,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.kind == 'FOR_ITER'
assert for_iter_node.type == 'FOR_ITER'
self.set_pos_info(for_iter_node, start, fin)
self.write(" for ")
self.preorder(designator)
@@ -830,7 +838,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
def n_listcomp(self, node):
self.write('[')
if node[0].kind == 'load_closure':
if node[0].type == 'load_closure':
self.listcomprehension_walk2(node)
else:
if node[0] == 'LOAD_LISTCOMP':
@@ -844,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].kind.startswith('JUMP_FORWARD')):
node[1].type.startswith('JUMP_FORWARD')):
self.set_pos_info(node[1], node[0][0].start, node[0][0].finish)
def setcomprehension_walk3(self, node, collection_index):
@@ -875,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].kind == 'expr':
if n[0].type == 'expr':
list_if = n
else:
list_if = n[1]
@@ -924,7 +932,10 @@ class FragmentsWalker(pysource.SourceWalker, object):
subclass = n.attr
break
pass
subclass_info = node if node == 'classdefdeco2' else node[0]
if node == 'classdefdeco2':
subclass_info = node
else:
subclass_info = node[0]
elif buildclass[1][0] == 'load_closure':
# Python 3 with closures not functions
load_closure = buildclass[1]
@@ -948,7 +959,10 @@ class FragmentsWalker(pysource.SourceWalker, object):
subclass = buildclass[1][0].attr
subclass_info = node[0]
else:
buildclass = node if (node == 'classdefdeco2') else node[0]
if node == 'classdefdeco2':
buildclass = node
else:
buildclass = node[0]
build_list = buildclass[1][0]
if hasattr(buildclass[-3][0], 'attr'):
subclass = buildclass[-3][0].attr
@@ -975,9 +989,9 @@ class FragmentsWalker(pysource.SourceWalker, object):
self.println(':')
# class body
self.indent_more()
self.indentMore()
self.build_class(subclass)
self.indent_less()
self.indentLess()
self.currentclass = cclass
self.set_pos_info(node, start, len(self.f.getvalue()))
@@ -1014,7 +1028,9 @@ class FragmentsWalker(pysource.SourceWalker, object):
tokens.append(Token('LAMBDA_MARKER'))
try:
ast = parser.parse(self.p, tokens, customize)
except (parser.ParserError, AssertionError) as e:
except parser.ParserError(e):
raise ParserError(e, tokens)
except AssertionError(e):
raise ParserError(e, tokens)
maybe_show_ast(self.showast, ast)
return ast
@@ -1030,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].kind == 'RETURN_VALUE':
if tokens[-2].kind != 'LOAD_CONST':
if tokens[-1].type == 'RETURN_VALUE':
if tokens[-2].type != 'LOAD_CONST':
tokens.append(Token('RETURN_LAST'))
if len(tokens) == 0:
return
@@ -1039,7 +1055,9 @@ class FragmentsWalker(pysource.SourceWalker, object):
# Build AST from disassembly.
try:
ast = parser.parse(self.p, tokens, customize)
except (parser.ParserError, AssertionError) as e:
except parser.ParserError(e):
raise ParserError(e, tokens)
except AssertionError(e):
raise ParserError(e, tokens)
maybe_show_ast(self.showast, ast)
@@ -1283,10 +1301,10 @@ class FragmentsWalker(pysource.SourceWalker, object):
# as a custom rule
start = len(self.f.getvalue())
n = len(node)-1
assert node[n].kind.startswith('CALL_FUNCTION')
assert node[n].type.startswith('CALL_FUNCTION')
for i in range(n-2, 0, -1):
if not node[i].kind in ['expr', 'LOAD_CLASSNAME']:
if not node[i].type in ['expr', 'LOAD_CLASSNAME']:
break
pass
@@ -1314,14 +1332,14 @@ class FragmentsWalker(pysource.SourceWalker, object):
p = self.prec
self.prec = 100
self.indent_more(INDENT_PER_LEVEL)
self.indentMore(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].kind.startswith('kvlist'):
if node[0].type.startswith('kvlist'):
# Python 3.5+ style key/value list in mapexpr
kv_node = node[0]
l = list(kv_node)
@@ -1336,11 +1354,11 @@ class FragmentsWalker(pysource.SourceWalker, object):
i += 2
pass
pass
elif node[1].kind.startswith('kvlist'):
elif node[1].type.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].kind == 'kv3':
if len(l) > 0 and l[0].type == 'kv3':
# Python 3.2 does this
kv_node = node[1][0]
l = list(kv_node)
@@ -1363,7 +1381,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
pass
else:
# Python 2 style kvlist
assert node[-1].kind.startswith('kvlist')
assert node[-1].type.startswith('kvlist')
kv_node = node[-1] # goto kvlist
for kv in kv_node:
@@ -1391,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.indent_less(INDENT_PER_LEVEL)
self.indentLess(INDENT_PER_LEVEL)
self.prec = p
self.prune()
@@ -1402,7 +1420,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
p = self.prec
self.prec = 100
n = node.pop()
lastnode = n.kind
lastnode = n.type
start = len(self.f.getvalue())
if lastnode.startswith('BUILD_LIST'):
self.write('['); endchar = ']'
@@ -1427,7 +1445,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
else:
flat_elems.append(elem)
self.indent_more(INDENT_PER_LEVEL)
self.indentMore(INDENT_PER_LEVEL)
if len(node) > 3:
line_separator = ',\n' + self.indent
else:
@@ -1452,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.indent_less(INDENT_PER_LEVEL)
self.indentLess(INDENT_PER_LEVEL)
self.prec = p
self.prune()
def template_engine(self, entry, startnode):
def 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("-----")
@@ -1496,8 +1514,8 @@ class FragmentsWalker(pysource.SourceWalker, object):
self.write('%')
self.set_pos_info(node, start, len(self.f.getvalue()))
elif typ == '+': self.indent_more()
elif typ == '-': self.indent_less()
elif typ == '+': self.indentMore()
elif typ == '-': self.indentLess()
elif typ == '|': self.write(self.indent)
# no longer used, since BUILD_TUPLE_n is pretty printed:
elif typ == 'r': recurse_node = True
@@ -1517,7 +1535,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
# 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.kind)
match = re.search(r'^for', startnode.type)
if match and entry[arg] == 3:
self.set_pos_info(node[0], start, finish)
for n in node[2]:
@@ -1611,7 +1629,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.kind)
match = re.search(r'^call_function', startnode.type)
if match:
last_node = startnode[-1]
# import traceback; traceback.print_stack()
@@ -1752,7 +1770,7 @@ if __name__ == '__main__':
nodeInfo = walk.offsets[name, offset]
node = nodeInfo.node
extractInfo = walk.extract_node_info(node)
print("code: %s" % node.kind)
print("code: %s" % node.type)
# print extractInfo
print(extractInfo.selectedText)
print(extractInfo.selectedLine)
@@ -1762,7 +1780,7 @@ if __name__ == '__main__':
print("Contained in...")
print(extractInfo.selectedLine)
print(extractInfo.markerLine)
print("code: %s" % p.kind)
print("code: %s" % p.type)
print('=' * 40)
pass
pass
@@ -1781,7 +1799,7 @@ if __name__ == '__main__':
nodeInfo = walk.offsets[name, offset]
node = nodeInfo.node
extractInfo = walk.extract_node_info(node)
print("code: %s" % node.kind)
print("code: %s" % node.type)
# print extractInfo
print(extractInfo.selectedText)
print(extractInfo.selectedLine)
@@ -1791,7 +1809,7 @@ if __name__ == '__main__':
print("Contained in...")
print(extractInfo.selectedLine)
print(extractInfo.markerLine)
print("code: %s" % p.kind)
print("code: %s" % p.type)
print('=' * 40)
pass
pass

View File

@@ -3,17 +3,12 @@
"""
All the crazy things we have to do to handle Python functions
"""
from xdis.code import iscode, code_has_star_arg, code_has_star_star_arg
from xdis.code import iscode
from uncompyle6.scanner import Code
from uncompyle6.parsers.astnode import AST
from uncompyle6 import PYTHON3
from uncompyle6.semantics.parser_error import ParserError
from uncompyle6.semantics.helper import print_docstring
if PYTHON3:
from itertools import zip_longest
else:
from itertools import izip_longest as zip_longest
from uncompyle6.show import maybe_show_ast_param_default
@@ -22,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.kind in ('STORE_GLOBAL', 'DELETE_GLOBAL', 'LOAD_GLOBAL'):
elif n.type in ('STORE_GLOBAL', 'DELETE_GLOBAL', 'LOAD_GLOBAL'):
globs.add(n.pattr)
return globs
@@ -31,7 +26,7 @@ def find_globals(node, globs):
for n in node:
if isinstance(n, AST):
globs = find_globals(n, globs)
elif n.kind in ('STORE_GLOBAL', 'DELETE_GLOBAL'):
elif n.type in ('STORE_GLOBAL', 'DELETE_GLOBAL'):
globs.add(n.pattr)
return globs
@@ -41,10 +36,21 @@ def find_none(node):
if n not in ('return_stmt', 'return_if_stmt'):
if find_none(n):
return True
elif n.kind == 'LOAD_CONST' and n.pattr is None:
elif n.type == 'LOAD_CONST' and n.pattr is None:
return True
return False
# FIXME: put this in xdis
def code_has_star_arg(code):
"""Return True iff
the code object has a variable positional parameter (*args-like)"""
return (code.co_flags & 4) != 0
def code_has_star_star_arg(code):
"""Return True iff
The code object has a variable keyword parameter (**kwargs-like)."""
return (code.co_flags & 8) != 0
# FIXME: DRY the below code...
def make_function3_annotate(self, node, isLambda, nested=1,
@@ -69,7 +75,7 @@ def make_function3_annotate(self, node, isLambda, nested=1,
return name
# MAKE_FUNCTION_... or MAKE_CLOSURE_...
assert node[-1].kind.startswith('MAKE_')
assert node[-1].type.startswith('MAKE_')
annotate_tuple = None
for annotate_last in range(len(node)-1, -1, -1):
@@ -85,7 +91,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].kind in ('annotate_arg' 'annotate_tuple'):
while j >= l and node[j].type in ('annotate_arg' 'annotate_tuple'):
annotate_args[annotate_tup[i]] = node[j][0]
i -= 1
j -= 1
@@ -111,7 +117,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].kind == 'LOAD_LAMBDA'
assert node[lambda_index].type == 'LOAD_LAMBDA'
code = node[lambda_index].attr
else:
code = codeNode.attr
@@ -128,7 +134,7 @@ def make_function3_annotate(self, node, isLambda, nested=1,
code._customize,
isLambda = isLambda,
noneInNames = ('None' in code.co_names))
except ParserError as p:
except ParserError, p:
self.write(str(p))
self.ERROR = p
return
@@ -152,14 +158,11 @@ def make_function3_annotate(self, node, isLambda, nested=1,
i = len(paramnames) - len(defparams)
suffix = ''
no_paramnames = len(paramnames[:i]) == 0
for param in paramnames[:i]:
self.write(suffix, param)
suffix = ', '
if param in annotate_tuple[0].attr:
p = annotate_tuple[0].attr.index(param)
p = [x for x in annotate_tuple[0].attr].index(param)
self.write(': ')
self.preorder(node[p])
if (line_number != self.line_number):
@@ -171,10 +174,12 @@ def make_function3_annotate(self, node, isLambda, nested=1,
# else:
# self.write(': %s' % value)
suffix = ', ' if i > 0 else ''
if i > 0:
suffix = ', '
else:
suffix = ''
for n in node:
if n == 'pos_arg':
no_paramnames = False
self.write(suffix)
param = paramnames[i]
self.write(param)
@@ -182,11 +187,7 @@ def make_function3_annotate(self, node, isLambda, nested=1,
aa = annotate_args[param]
if isinstance(aa, tuple):
aa = aa[0]
self.write(': "%s"' % aa)
elif isinstance(aa, AST):
self.write(': ')
self.preorder(aa)
self.write(': "%s"' % aa)
self.write('=')
i += 1
self.preorder(n)
@@ -199,65 +200,64 @@ def make_function3_annotate(self, node, isLambda, nested=1,
# self.println(indent, '#flags:\t', int(code.co_flags))
if kw_args + annotate_argc > 0:
if no_paramnames:
if not code_has_star_arg(code):
if argc > 0:
self.write(", *, ")
else:
self.write("*, ")
pass
else:
self.write(", ")
if not code_has_star_arg(code):
if argc > 0:
kwargs = node[0]
last = len(kwargs)-1
i = 0
for n in node[0]:
if n == 'kwarg':
if (line_number != self.line_number):
self.write("\n" + indent)
line_number = self.line_number
self.write('%s=' % n[0].pattr)
self.preorder(n[1])
if i < last:
self.write(', ')
i += 1
pass
pass
annotate_args = []
for n in node:
if n == 'annotate_arg':
annotate_args.append(n[0])
elif n == 'annotate_tuple':
t = n[0].attr
if t[-1] == 'return':
t = t[0:-1]
annotate_args = annotate_args[:-1]
pass
last = len(annotate_args) - 1
for i in range(len(annotate_args)):
self.write("%s: " % (t[i]))
self.preorder(annotate_args[i])
if i < last:
self.write(', ')
pass
pass
break
self.write(", *, ")
else:
self.write("*, ")
pass
else:
self.write(", ")
kwargs = node[0]
last = len(kwargs)-1
i = 0
for n in node[0]:
if n == 'kwarg':
if (line_number != self.line_number):
self.write("\n" + indent)
line_number = self.line_number
self.write('%s=' % n[0].pattr)
self.preorder(n[1])
if i < last:
self.write(', ')
i += 1
pass
pass
annotate_args = []
for n in node:
if n == 'annotate_arg':
annotate_args.append(n[0])
elif n == 'annotate_tuple':
t = n[0].attr
if t[-1] == 'return':
t = t[0:-1]
annotate_args = annotate_args[:-1]
pass
last = len(annotate_args) - 1
for i in range(len(annotate_args)):
self.write("%s: " % (t[i]))
self.preorder(annotate_args[i])
if i < last:
self.write(', ')
pass
pass
break
pass
pass
if code_has_star_star_arg(code):
if argc > 0:
self.write(', ')
self.write('**%s' % code.co_varnames[argc + kw_pairs])
if code_has_star_star_arg(code):
if argc > 0:
self.write(', ')
self.write('**%s' % code.co_varnames[argc + kw_pairs])
if isLambda:
self.write(": ")
else:
self.write(')')
if 'return' in annotate_tuple[0].attr:
if (line_number != self.line_number) and not no_paramnames:
if (line_number != self.line_number):
self.write("\n" + indent)
line_number = self.line_number
self.write(' -> ')
@@ -320,7 +320,7 @@ def make_function2(self, node, isLambda, nested=1, codeNode=None):
return name
# MAKE_FUNCTION_... or MAKE_CLOSURE_...
assert node[-1].kind.startswith('MAKE_')
assert node[-1].type.startswith('MAKE_')
args_node = node[-1]
if isinstance(args_node.attr, tuple):
@@ -336,7 +336,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].kind == 'LOAD_LAMBDA'
assert node[lambda_index].type == 'LOAD_LAMBDA'
code = node[lambda_index].attr
else:
code = codeNode.attr
@@ -356,17 +356,21 @@ def make_function2(self, node, isLambda, nested=1, codeNode=None):
code._customize,
isLambda = isLambda,
noneInNames = ('None' in code.co_names))
except ParserError as p:
except ParserError, p:
self.write(str(p))
self.ERROR = p
return
kw_pairs = args_node.attr[1] if self.version >= 3.0 else 0
if self.version >= 3.0:
kw_pairs = args_node.attr[1]
else:
kw_pairs = 0
indent = self.indent
# build parameters
tup = [paramnames, defparams]
params = [build_param(ast, name, default) for
name, default in zip_longest(paramnames, defparams, fillvalue=None)]
name, default in map(lambda *tup:tup, *tup)]
params.reverse() # back to correct order
if code_has_star_arg(code):
@@ -400,8 +404,7 @@ def make_function2(self, node, isLambda, nested=1, codeNode=None):
if code_has_star_star_arg(code):
if argc > 0:
self.write(', ')
if argc + kw_pairs > 0:
self.write('**%s' % code.co_varnames[argc + kw_pairs])
self.write('**%s' % code.co_varnames[argc + kw_pairs])
if isLambda:
self.write(": ")
@@ -427,34 +430,10 @@ def make_function2(self, node, isLambda, nested=1, codeNode=None):
def make_function3(self, node, isLambda, nested=1, codeNode=None):
"""Dump function definition, doc string, and function body in
Python version 3.0 and above
"""
"""Dump function definition, doc string, and function body."""
# For Python 3.3, the evaluation stack in MAKE_FUNCTION is:
# * default argument objects in positional order
# * pairs of name and default argument, with the name just below
# the object on the stack, for keyword-only parameters
# * parameter annotation objects
# * a tuple listing the parameter names for the annotations
# (only if there are ony annotation objects)
# * the code associated with the function (at TOS1)
# * the qualified name of the function (at TOS)
# For Python 3.0 .. 3.2 the evaluation stack is:
# The function object is defined to have argc default parameters,
# which are found below TOS.
# * first come positional args in the order they are given in the source,
# * next come the keyword args in the order they given in the source,
# * finally is the code associated with the function (at TOS)
#
# Note: There is no qualified name at TOS
# MAKE_CLOSURE adds an additional closure slot
# Thank you, Python, for a such a well-thought out system that has
# changed 4 or so times.
# FIXME: call make_function3 if we are self.version >= 3.0
# and then simplify the below.
def build_param(ast, name, default):
"""build parameters:
@@ -472,33 +451,23 @@ def make_function3(self, node, isLambda, nested=1, codeNode=None):
return name
# MAKE_FUNCTION_... or MAKE_CLOSURE_...
assert node[-1].kind.startswith('MAKE_')
# Python 3.3+ adds a qualified name at TOS (-1)
# moving down the LOAD_LAMBDA instruction
if 3.0 <= self.version <= 3.2:
lambda_index = -2
elif 3.03 <= self.version:
lambda_index = -3
else:
lambda_index = None
assert node[-1].type.startswith('MAKE_')
args_node = node[-1]
if isinstance(args_node.attr, tuple):
pos_args, kw_args, annotate_argc = args_node.attr
if self.version <= 3.3 and len(node) > 2 and node[lambda_index] != 'LOAD_LAMBDA':
# args are after kwargs; kwargs are bundled as one node
if self.version <= 3.3 and len(node) > 2 and node[-3] != 'LOAD_LAMBDA':
# positional args are after kwargs
defparams = node[1:args_node.attr[0]+1]
else:
# args are before kwargs; kwags as bundled as one node
# positional args are before kwargs
defparams = node[:args_node.attr[0]]
pos_args, kw_args, annotate_argc = args_node.attr
else:
if self.version < 3.6:
defparams = node[:args_node.attr]
else:
default, kw, annotate, closure = args_node.attr
# FIXME: start here for Python 3.6 and above:
# FIXME: start here.
defparams = []
# if default:
# defparams = node[-(2 + kw + annotate + closure)]
@@ -508,9 +477,15 @@ def make_function3(self, node, isLambda, nested=1, codeNode=None):
kw_args = 0
pass
if 3.0 <= self.version <= 3.2:
lambda_index = -2
elif 3.03 <= self.version:
lambda_index = -3
else:
lambda_index = None
if lambda_index and isLambda and iscode(node[lambda_index].attr):
assert node[lambda_index].kind == 'LOAD_LAMBDA'
assert node[lambda_index].type == 'LOAD_LAMBDA'
code = node[lambda_index].attr
else:
code = codeNode.attr
@@ -523,7 +498,7 @@ def make_function3(self, node, isLambda, nested=1, codeNode=None):
paramnames = list(code.co_varnames[:argc])
# defaults are for last n parameters, thus reverse
if not 3.0 <= self.version <= 3.1:
if not 3.0 <= self.version <= 3.2:
paramnames.reverse(); defparams.reverse()
try:
@@ -531,33 +506,71 @@ def make_function3(self, node, isLambda, nested=1, codeNode=None):
code._customize,
isLambda = isLambda,
noneInNames = ('None' in code.co_names))
except ParserError as p:
except ParserError, p:
self.write(str(p))
self.ERROR = p
return
kw_pairs = args_node.attr[1] if self.version >= 3.0 else 0
if self.version >= 3.0:
kw_pairs = args_node.attr[1]
else:
kw_pairs = 0
indent = self.indent
# build parameters
params = [build_param(ast, name, d) for
name, d in zip_longest(paramnames, defparams, fillvalue=None)]
if not 3.0 <= self.version <= 3.1:
if self.version != 3.2:
tup = [paramnames, defparams]
params = [build_param(ast, name, default) for
name, default in map(lambda *tup:tup, *tup)]
params.reverse() # back to correct order
if code_has_star_arg(code):
if self.version > 3.0:
params.append('*%s' % code.co_varnames[argc + kw_pairs])
else:
params.append('*%s' % code.co_varnames[argc])
argc += 1
if code_has_star_arg(code):
if self.version > 3.0:
params.append('*%s' % code.co_varnames[argc + kw_pairs])
else:
params.append('*%s' % code.co_varnames[argc])
argc += 1
# dump parameter list (with default values)
if isLambda:
self.write("lambda ", ", ".join(params))
else:
self.write("(", ", ".join(params))
# self.println(indent, '#flags:\t', int(code.co_flags))
# dump parameter list (with default values)
if isLambda:
self.write("lambda ", ", ".join(params))
else:
self.write("(", ", ".join(params))
# self.println(indent, '#flags:\t', int(code.co_flags))
if isLambda:
self.write("lambda ")
else:
self.write("(")
pass
last_line = self.f.getvalue().split("\n")[-1]
l = len(last_line)
indent = ' ' * l
line_number = self.line_number
if code_has_star_arg(code):
self.write('*%s' % code.co_varnames[argc + kw_pairs])
argc += 1
i = len(paramnames) - len(defparams)
self.write(", ".join(paramnames[:i]))
if i > 0:
suffix = ', '
else:
suffix = ''
for n in node:
if n == 'pos_arg':
self.write(suffix)
self.write(paramnames[i] + '=')
i += 1
self.preorder(n)
if (line_number != self.line_number):
suffix = ",\n" + indent
line_number = self.line_number
else:
suffix = ', '
if kw_args > 0:
if not (4 & code.co_flags):
@@ -573,7 +586,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.kind in ('kwargs', 'kwarg')):
elif self.version >= 3.4 and not (n.type in ('kwargs', 'kwarg')):
continue
else:
self.preorder(n)

View File

@@ -11,104 +11,62 @@ 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 representing a node index.
#
# %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.
from __future__ import print_function
import sys
from uncompyle6 import PYTHON3
@@ -145,7 +103,7 @@ else:
def is_docstring(node):
try:
return (node[0][0].kind == 'assign' and
return (node[0][0].type == 'assign' and
node[0][0][1][0].pattr == '__doc__')
except:
return False
@@ -164,29 +122,6 @@ class SourceWalker(GenericASTTraversal, object):
debug_parser=PARSER_DEFAULT_DEBUG,
compile_mode='exec', is_pypy=False,
linestarts={}):
"""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 = {
@@ -369,18 +304,11 @@ class SourceWalker(GenericASTTraversal, object):
# MAKE_FUNCTION ..
code = node[-3]
self.indent_more()
self.indentMore()
for annotate_last in range(len(node)-1, -1, -1):
if node[annotate_last] == 'annotate_tuple':
break
# FIXME: the real situation is that when derived from
# funcdef_annotate we the name has been filled in.
# But when derived from funcdefdeco it hasn't Would like a better
# way to distinquish.
if self.f.getvalue()[-4:] == 'def ':
self.write(code.attr.co_name)
# FIXME: handle and pass full annotate args
make_function3_annotate(self, node, isLambda=False,
codeNode=code, annotate_last=annotate_last)
@@ -389,7 +317,7 @@ class SourceWalker(GenericASTTraversal, object):
self.write('\n\n')
else:
self.write('\n\n\n')
self.indent_less()
self.indentLess()
self.prune() # stop recursing
self.n_mkfunc_annotate = n_mkfunc_annotate
@@ -421,13 +349,11 @@ class SourceWalker(GenericASTTraversal, object):
})
def n_async_call_function(node):
self.f.write('async ')
node.kind == 'call_function'
node.type == 'call_function'
p = self.prec
self.prec = 80
self.template_engine(('%c(%P)', 0,
(1, -4, ', ', 100)), node)
self.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
@@ -440,13 +366,13 @@ class SourceWalker(GenericASTTraversal, object):
for i in mapping[1:]:
key = key[i]
pass
if key.kind.startswith('CALL_FUNCTION_VAR_KW'):
if key.type.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.kind]
entry = table[key.type]
kwarg_pos = entry[2][1]
args_pos = kwarg_pos - 1
# Put last node[args_pos] after subsequent kwargs
@@ -467,11 +393,9 @@ 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.template_engine(('\n\n%|async def %c\n',
-2), node)
self.engine(('\n\n%|async def %c\n', -2), node)
else:
self.template_engine(('\n\n%|def %c\n', -2),
node)
self.engine(('\n\n%|def %c\n', -2), node)
self.prune()
self.n_funcdef = n_funcdef
@@ -569,10 +493,10 @@ class SourceWalker(GenericASTTraversal, object):
super(SourceWalker, self).preorder(node)
self.set_pos_info(node)
def indent_more(self, indent=TAB):
def indentMore(self, indent=TAB):
self.indent += indent
def indent_less(self, indent=TAB):
def indentLess(self, indent=TAB):
self.indent = self.indent[:-len(indent)]
def traverse(self, node, indent=None, isLambda=False):
@@ -623,8 +547,6 @@ class SourceWalker(GenericASTTraversal, object):
if self.pending_newlines:
out = out[:-self.pending_newlines]
if isinstance(out, str) and not PYTHON3:
out = unicode(out, 'utf-8')
self.f.write(out)
def println(self, *data):
@@ -647,19 +569,32 @@ 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()
## The below doesn't work because continue may be the only thing inside an 'else'. For example
# for ...
# if ...
# else:
# continue
#
# def n_continue_stmt(self, node):
# if self.version >= 3.0 and node[0] == 'CONTINUE':
# t = node[0]
# if not t.linestart:
# # Artificially-added "continue" statements derived from JUMP_ABSOLUTE
# # don't have line numbers associated with them.
# # If this is a CONTINUE is to the same target as a JUMP_ABSOLUTE following it,
# # then the "continue" can be suppressed.
# op, offset = t.op, t.offset
# next_offset = self.scanner.next_offset(op, offset)
# scanner = self.scanner
# code = scanner.code
# if next_offset < len(code):
# next_inst = code[next_offset]
# if (scanner.opc.opname[next_inst] == 'JUMP_ABSOLUTE'
# and t.pattr == code[next_offset+1]):
# # Suppress "continue"
# import pdb; pdb.set_trace()
# self.prune()
# self.default(node)
def n_return_stmt(self, node):
if self.params['isLambda']:
@@ -678,7 +613,6 @@ 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:
@@ -735,12 +669,12 @@ class SourceWalker(GenericASTTraversal, object):
def n_expr(self, node):
p = self.prec
if node[0].kind.startswith('binary_expr'):
if node[0].type.startswith('binary_expr'):
n = node[0][-1][0]
else:
n = node[0]
self.prec = PRECEDENCE.get(n.kind, -2)
self.prec = PRECEDENCE.get(n.type, -2)
if n == 'LOAD_CONST' and repr(n.pattr)[0] == '-':
self.prec = 6
@@ -823,9 +757,9 @@ class SourceWalker(GenericASTTraversal, object):
self.prune()
def n_delete_subscr(self, node):
if node[-2][0] == 'build_list' and node[-2][0][-1].kind.startswith('BUILD_TUPLE'):
if node[-2][0] == 'build_list' and node[-2][0][-1].type.startswith('BUILD_TUPLE'):
if node[-2][0][-1] != 'BUILD_TUPLE_0':
node[-2][0].kind = 'build_tuple2'
node[-2][0].type = 'build_tuple2'
self.default(node)
n_store_subscr = n_binary_subscr = n_delete_subscr
@@ -834,9 +768,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].kind = 'tf_trystmt'
node[1][0][0][0].type = 'tf_trystmt'
if node[1][0][0][0] == 'tryelsestmt':
node[1][0][0][0].kind = 'tf_tryelsestmt'
node[1][0][0][0].type = 'tf_tryelsestmt'
self.default(node)
def n_exec_stmt(self, node):
@@ -861,26 +795,26 @@ class SourceWalker(GenericASTTraversal, object):
if len(n) == 1 == len(n[0]) and n[0] == '_stmts':
n = n[0][0][0]
elif n[0].kind in ('lastc_stmt', 'lastl_stmt'):
elif n[0].type in ('lastc_stmt', 'lastl_stmt'):
n = n[0][0]
else:
if not preprocess:
self.default(node)
return
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'
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'
self.n_ifelsestmt(n, preprocess=True)
if n == 'ifelifstmt':
n.kind = 'elifelifstmt'
elif n.kind in ('ifelsestmt', 'ifelsestmtc', 'ifelsestmtl'):
n.kind = 'elifelsestmt'
n.type = 'elifelifstmt'
elif n.type in ('ifelsestmt', 'ifelsestmtc', 'ifelsestmtl'):
n.type = 'elifelsestmt'
if not preprocess:
self.default(node)
@@ -889,7 +823,7 @@ class SourceWalker(GenericASTTraversal, object):
def n_ifelsestmtr(self, node):
if node[2] == 'COME_FROM':
return_stmts_node = node[3]
node.kind = 'ifelsestmtr2'
node.type = 'ifelsestmtr2'
else:
return_stmts_node = node[2]
if len(return_stmts_node) != 2:
@@ -905,9 +839,9 @@ class SourceWalker(GenericASTTraversal, object):
self.write(self.indent, 'if ')
self.preorder(node[0])
self.println(':')
self.indent_more()
self.indentMore()
self.preorder(node[1])
self.indent_less()
self.indentLess()
if_ret_at_end = False
if len(return_stmts_node[0]) >= 3:
@@ -920,27 +854,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].kind = 'elifstmt'
n[0].type = '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.indent_more()
self.indentMore()
past_else = True
self.preorder(n)
if not past_else or if_ret_at_end:
self.println(self.indent, 'else:')
self.indent_more()
self.indentMore()
self.preorder(return_stmts_node[1])
self.indent_less()
self.indentLess()
self.prune()
n_ifelsestmtr2 = n_ifelsestmtr
def n_elifelsestmtr(self, node):
if node[2] == 'COME_FROM':
return_stmts_node = node[3]
node.kind = 'elifelsestmtr2'
node.type = 'elifelsestmtr2'
else:
return_stmts_node = node[2]
@@ -955,22 +889,22 @@ class SourceWalker(GenericASTTraversal, object):
self.write(self.indent, 'elif ')
self.preorder(node[0])
self.println(':')
self.indent_more()
self.indentMore()
self.preorder(node[1])
self.indent_less()
self.indentLess()
for n in return_stmts_node[0]:
n[0].kind = 'elifstmt'
n[0].type = 'elifstmt'
self.preorder(n)
self.println(self.indent, 'else:')
self.indent_more()
self.indentMore()
self.preorder(return_stmts_node[1])
self.indent_less()
self.indentLess()
self.prune()
def n_import_as(self, node):
store_node = node[-1][-1]
assert store_node.kind.startswith('STORE_')
assert store_node.type.startswith('STORE_')
iname = node[0].pattr # import name
sname = store_node.pattr # store_name
if iname and iname == sname or iname.startswith(sname + '.'):
@@ -1006,14 +940,14 @@ class SourceWalker(GenericASTTraversal, object):
func_name = code_node.attr.co_name
self.write(func_name)
self.indent_more()
self.indentMore()
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.indent_less()
self.indentLess()
self.prune() # stop recursing
def make_function(self, node, isLambda, nested=1,
@@ -1038,7 +972,10 @@ class SourceWalker(GenericASTTraversal, object):
return
n = node[-1]
elif node[-1] == 'del_stmt':
n = node[-3] if node[-2] == 'JUMP_BACK' else node[-2]
if node[-2] == 'JUMP_BACK':
n = node[-3]
else:
n = node[-2]
assert n == 'list_iter'
@@ -1056,7 +993,10 @@ class SourceWalker(GenericASTTraversal, object):
list_iter = node[-1]
else:
expr = n[1]
list_iter = node[-3] if node[-2] == 'JUMP_BACK' else node[-2]
if node[-2] == 'JUMP_BACK':
list_iter = node[-3]
else:
list_iter = node[-2]
assert expr == 'expr'
assert list_iter == 'list_iter'
@@ -1084,7 +1024,7 @@ class SourceWalker(GenericASTTraversal, object):
"""
p = self.prec
self.prec = 27
if node[-1].kind == 'list_iter':
if node[-1].type == 'list_iter':
n = node[-1]
elif self.is_pypy and node[-1] == 'JUMP_BACK':
n = node[-2]
@@ -1108,7 +1048,10 @@ class SourceWalker(GenericASTTraversal, object):
self.write( '[ ')
expr = n[0]
list_iter = node[-2] if self.is_pypy and node[-1] == 'JUMP_BACK' else node[-1]
if self.is_pypy and node[-1] == 'JUMP_BACK':
list_iter = node[-2]
else:
list_iter = node[-1]
assert expr == 'expr'
assert list_iter == 'list_iter'
@@ -1182,7 +1125,10 @@ class SourceWalker(GenericASTTraversal, object):
self.write(' for ')
self.preorder(ast[iter_index-1])
self.write(' in ')
iter_expr = node[2] if node[2] == 'expr' else node[-3]
if node[2] == 'expr':
iter_expr = node[2]
else:
iter_expr = node[-3]
assert iter_expr == 'expr'
self.preorder(iter_expr)
self.preorder(ast[iter_index])
@@ -1190,7 +1136,10 @@ class SourceWalker(GenericASTTraversal, object):
def n_genexpr(self, node):
self.write('(')
code_index = -6 if self.version > 3.2 else -5
if self.version > 3.2:
code_index = -6
else:
code_index = -5
self.comprehension_walk(node, iter_index=3, code_index=code_index)
self.write(')')
self.prune()
@@ -1199,7 +1148,7 @@ class SourceWalker(GenericASTTraversal, object):
self.write('{')
if node[0] in ['LOAD_SETCOMP', 'LOAD_DICTCOMP']:
self.comprehension_walk3(node, 1, 0)
elif node[0].kind == 'load_closure' and self.version >= 3.0:
elif node[0].type == 'load_closure' and self.version >= 3.0:
self.setcomprehension_walk3(node, collection_index=4)
else:
self.comprehension_walk(node, iter_index=4)
@@ -1266,7 +1215,7 @@ class SourceWalker(GenericASTTraversal, object):
# Python 2.7+ starts including set_comp_body
# Python 3.5+ starts including setcomp_func
assert n.kind in ('lc_body', 'comp_body', 'setcomp_func', 'set_comp_body'), ast
assert n.type 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])
@@ -1316,7 +1265,7 @@ class SourceWalker(GenericASTTraversal, object):
n = n[3]
elif n in ('list_if', 'list_if_not'):
# FIXME: just a guess
if n[0].kind == 'expr':
if n[0].type == 'expr':
list_if = n
else:
list_if = n[1]
@@ -1337,7 +1286,7 @@ class SourceWalker(GenericASTTraversal, object):
def n_listcomp(self, node):
self.write('[')
if node[0].kind == 'load_closure':
if node[0].type == 'load_closure':
self.listcomprehension_walk2(node)
else:
self.comprehension_walk3(node, 1, 0)
@@ -1374,7 +1323,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].kind == 'expr':
if n[0].type == 'expr':
list_if = n
else:
list_if = n[1]
@@ -1431,7 +1380,10 @@ class SourceWalker(GenericASTTraversal, object):
break
pass
pass
subclass_info = node if node == 'classdefdeco2' else node[0]
if node == 'classdefdeco2':
subclass_info = node
else:
subclass_info = node[0]
elif buildclass[1][0] == 'load_closure':
# Python 3 with closures not functions
load_closure = buildclass[1]
@@ -1458,7 +1410,10 @@ class SourceWalker(GenericASTTraversal, object):
subclass_code = buildclass[1][0].attr
subclass_info = node[0]
else:
buildclass = node if (node == 'classdefdeco2') else node[0]
if node == 'classdefdeco2':
buildclass = node
else:
buildclass = node[0]
build_list = buildclass[1][0]
if hasattr(buildclass[-3][0], 'attr'):
subclass_code = buildclass[-3][0].attr
@@ -1484,9 +1439,9 @@ class SourceWalker(GenericASTTraversal, object):
self.println(':')
# class body
self.indent_more()
self.indentMore()
self.build_class(subclass_code)
self.indent_less()
self.indentLess()
self.currentclass = cclass
if len(self.param_stack) > 1:
@@ -1520,10 +1475,10 @@ class SourceWalker(GenericASTTraversal, object):
def print_super_classes3(self, node):
n = len(node)-1
if node.kind != 'expr':
assert node[n].kind.startswith('CALL_FUNCTION')
if node.type != 'expr':
assert node[n].type.startswith('CALL_FUNCTION')
for i in range(n-2, 0, -1):
if not node[i].kind in ['expr', 'LOAD_CLASSNAME']:
if not node[i].type in ['expr', 'LOAD_CLASSNAME']:
break
pass
@@ -1557,13 +1512,13 @@ class SourceWalker(GenericASTTraversal, object):
p = self.prec
self.prec = 100
self.indent_more(INDENT_PER_LEVEL)
self.indentMore(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].kind.startswith('kvlist'):
if node[0].type.startswith('kvlist'):
# Python 3.5+ style key/value list in mapexpr
kv_node = node[0]
l = list(kv_node)
@@ -1586,11 +1541,11 @@ class SourceWalker(GenericASTTraversal, object):
i += 2
pass
pass
elif len(node) > 1 and node[1].kind.startswith('kvlist'):
elif len(node) > 1 and node[1].type.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].kind == 'kv3':
if len(l) > 0 and l[0].type == 'kv3':
# Python 3.2 does this
kv_node = node[1][0]
l = list(kv_node)
@@ -1615,7 +1570,7 @@ class SourceWalker(GenericASTTraversal, object):
i += 3
pass
pass
elif node[-1].kind.startswith('BUILD_CONST_KEY_MAP'):
elif node[-1].type.startswith('BUILD_CONST_KEY_MAP'):
# Python 3.6+ style const map
keys = node[-2].pattr
values = node[:-2]
@@ -1640,7 +1595,7 @@ class SourceWalker(GenericASTTraversal, object):
pass
else:
# Python 2 style kvlist
assert node[-1].kind.startswith('kvlist')
assert node[-1].type.startswith('kvlist')
kv_node = node[-1] # goto kvlist
first_time = True
@@ -1695,7 +1650,7 @@ class SourceWalker(GenericASTTraversal, object):
if sep.startswith(",\n"):
self.write(sep[1:])
self.write('}')
self.indent_less(INDENT_PER_LEVEL)
self.indentLess(INDENT_PER_LEVEL)
self.prec = p
self.prune()
@@ -1706,7 +1661,7 @@ class SourceWalker(GenericASTTraversal, object):
p = self.prec
self.prec = 100
lastnode = node.pop()
lastnodetype = lastnode.kind
lastnodetype = lastnode.type
# If this build list is inside a CALL_FUNCTION_VAR,
# then the first * has already been printed.
@@ -1746,7 +1701,7 @@ class SourceWalker(GenericASTTraversal, object):
else:
flat_elems.append(elem)
self.indent_more(INDENT_PER_LEVEL)
self.indentMore(INDENT_PER_LEVEL)
sep = ''
for elem in flat_elems:
@@ -1771,12 +1726,12 @@ class SourceWalker(GenericASTTraversal, object):
if lastnode.attr == 1 and lastnodetype.startswith('BUILD_TUPLE'):
self.write(',')
self.write(endchar)
self.indent_less(INDENT_PER_LEVEL)
self.indentLess(INDENT_PER_LEVEL)
self.prec = p
self.prune()
def n_unpack(self, node):
if node[0].kind.startswith('UNPACK_EX'):
if node[0].type.startswith('UNPACK_EX'):
# Python 3+
before_count, after_count = node[0].attr
for i in range(before_count+1):
@@ -1791,8 +1746,8 @@ class SourceWalker(GenericASTTraversal, object):
self.prune()
return
for n in node[1:]:
if n[0].kind == 'unpack':
n[0].kind = 'unpack_w_parens'
if n[0].type == 'unpack':
n[0].type = 'unpack_w_parens'
self.default(node)
n_unpack_w_parens = n_unpack
@@ -1801,34 +1756,33 @@ 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].kind == 'STORE_LOCALS'):
node[1][0].type == 'STORE_LOCALS'):
self.prune()
self.default(node)
def n_assign2(self, node):
for n in node[-2:]:
if n[0] == 'unpack':
n[0].kind = 'unpack_w_parens'
n[0].type = 'unpack_w_parens'
self.default(node)
def n_assign3(self, node):
for n in node[-3:]:
if n[0] == 'unpack':
n[0].kind = 'unpack_w_parens'
n[0].type = 'unpack_w_parens'
self.default(node)
def n_except_cond2(self, node):
if node[-2][0] == 'unpack':
node[-2][0].kind = 'unpack_w_parens'
node[-2][0].type = 'unpack_w_parens'
self.default(node)
def template_engine(self, entry, startnode):
def 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.kind, ', ', entry[0])
# self.println("----> ", startnode.type, ', ', entry[0])
fmt = entry[0]
arg = 1
i = 0
@@ -1846,23 +1800,24 @@ class SourceWalker(GenericASTTraversal, object):
if typ == '%': self.write('%')
elif typ == '+':
self.line_number += 1
self.indent_more()
self.indentMore()
elif typ == '-':
self.line_number += 1
self.indent_less()
self.indentLess()
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.kind in ('unpack', 'unpack_w_parens') and
if (node.type in ('unpack', 'unpack_w_parens') and
node[0].attr == 1):
self.write(',')
elif typ == 'c':
entry_node = node[entry[arg]]
self.preorder(entry_node)
arg += 1
if isinstance(entry[arg], int):
entry_node = node[entry[arg]]
self.preorder(entry_node)
arg += 1
elif typ == 'p':
p = self.prec
(index, self.prec) = entry[arg]
@@ -1928,8 +1883,8 @@ class SourceWalker(GenericASTTraversal, object):
key = key[i]
pass
if key.kind in table:
self.template_engine(table[key.kind], node)
if key.type in table:
self.engine(table[key.type], node)
self.prune()
def customize(self, customize):
@@ -1953,7 +1908,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 the None in this
p2 = (0, 0, None) # .. because of this
else:
str = '%c(%C, '
p2 = (1, -2, ', ')
@@ -2148,15 +2103,12 @@ 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)
except (python_parser.ParserError, AssertionError) as e:
except python_parser.ParserError, e:
raise ParserError(e, tokens)
except AssertionError, e:
raise ParserError(e, tokens)
maybe_show_ast(self.showast, ast)
return ast
@@ -2167,10 +2119,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].kind in ('RETURN_VALUE', 'RETURN_VALUE_LAMBDA'):
if tokens[-1].type == 'RETURN_VALUE':
# Python 3.4's classes can add a "return None" which is
# invalid syntax.
if tokens[-2].kind == 'LOAD_CONST':
if tokens[-2].type == 'LOAD_CONST':
if isTopLevel or tokens[-2].pattr is None:
del tokens[-2:]
else:
@@ -2183,7 +2135,9 @@ class SourceWalker(GenericASTTraversal, object):
# Build AST from disassembly.
try:
ast = python_parser.parse(self.p, tokens, customize)
except (python_parser.ParserError, AssertionError) as e:
except python_parser.ParserError, e:
raise ParserError(e, tokens)
except AssertionError, e:
raise ParserError(e, tokens)
maybe_show_ast(self.showast, ast)
@@ -2212,7 +2166,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'] = 'full'
debug_parser['errorstack'] = True
# Build AST from disassembly.
linestarts = dict(scanner.opc.findlinestarts(co))
@@ -2261,10 +2215,10 @@ def deparse_code(version, co, out=sys.stdout, showasm=None, showast=False,
if __name__ == '__main__':
def deparse_test(co):
"This is a docstring"
sys_version = sys.version_info.major + (sys.version_info.minor / 10.0)
sys_version = float(sys.version[0:3])
deparsed = deparse_code(sys_version, co, showasm='after', showast=True)
# deparsed = deparse_code(sys_version, co, showasm=None, showast=False,
# showgrammar=True)
print(deparsed.text)
return
deparse_test(deparse_test.__code__)
deparse_test(deparse_test.func_code)

View File

@@ -12,7 +12,10 @@ def maybe_show_asm(showasm, tokens):
:param tokens: The asm tokens to show.
"""
if showasm:
stream = showasm if hasattr(showasm, 'write') else sys.stdout
if hasattr(showasm, 'write'):
stream = showasm
else:
stream = sys.stdout
for t in tokens:
stream.write(str(t))
stream.write('\n')
@@ -29,7 +32,10 @@ def maybe_show_ast(showast, ast):
:param ast: The ast to show.
"""
if showast:
stream = showast if hasattr(showast, 'write') else sys.stdout
if hasattr(showast, 'write'):
stream = showast
else:
stream = sys.stdout
stream.write(str(ast))
stream.write('\n')
@@ -48,7 +54,10 @@ def maybe_show_ast_param_default(showast, name, default):
:param default: The function parameter default.
"""
if showast:
stream = showast if hasattr(showast, 'write') else sys.stdout
if hasattr(showast, 'write'):
stream = showast
else:
stream = sys.stdout
stream.write('\n')
stream.write('--' + name)
stream.write('\n')

View File

@@ -1,13 +1,11 @@
#
# (C) Copyright 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
# (C) Copyright 2015-2017 by Rocky Bernstein
# (C) Copyright 2015-2016 by Rocky Bernstein
#
"""
byte-code verification
"""
from __future__ import print_function
import dis, operator
import uncompyle6
@@ -45,7 +43,7 @@ BIN_OP_FUNCS = {
'BINARY_OR': operator.or_,
}
JUMP_OPS = None
JUMP_OPs = None
# --- exceptions ---
@@ -227,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,
@@ -244,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.kind != 'COME_FROM']
tokens2 = [t for t in tokens2 if t.kind != 'COME_FROM']
tokens1 = [t for t in tokens1 if t.type != 'COME_FROM']
tokens2 = [t for t in tokens2 if t.type != '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].kind == 'RETURN_VALUE' \
and tokens1[-2].kind == 'LOAD_CONST' \
and tokens1[-1].type == 'RETURN_VALUE' \
and tokens1[-2].type == 'LOAD_CONST' \
and tokens1[-2].pattr is None \
and tokens1[-3].kind == 'RETURN_VALUE':
and tokens1[-3].type == 'RETURN_VALUE':
break
else:
raise CmpErrorCodeLen(name, tokens1, tokens2)
@@ -267,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].kind != tokens2[i2].kind:
if tokens1[i1].kind == 'LOAD_CONST' == tokens2[i2].kind:
if tokens1[i1].type != tokens2[i2].type:
if tokens1[i1].type == 'LOAD_CONST' == tokens2[i2].type:
i = 1
while tokens1[i1+i].kind == 'LOAD_CONST':
while tokens1[i1+i].type == 'LOAD_CONST':
i += 1
if tokens1[i1+i].kind.startswith(('BUILD_TUPLE', 'BUILD_LIST')) \
and i == int(tokens1[i1+i].kind.split('_')[-1]):
if tokens1[i1+i].type.startswith(('BUILD_TUPLE', 'BUILD_LIST')) \
and i == int(tokens1[i1+i].type.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],
@@ -281,60 +279,60 @@ def cmp_code_objects(version, is_pypy, code_obj1, code_obj2,
i1 += i + 1
i2 += 1
continue
elif i == 2 and tokens1[i1+i].kind == 'ROT_TWO' and tokens2[i2+1].kind == 'UNPACK_SEQUENCE_2':
elif i == 2 and tokens1[i1+i].type == 'ROT_TWO' and tokens2[i2+1].type == 'UNPACK_SEQUENCE_2':
i1 += 3
i2 += 2
continue
elif i == 2 and tokens1[i1+i].kind in BIN_OP_FUNCS:
f = BIN_OP_FUNCS[tokens1[i1+i].kind]
elif i == 2 and tokens1[i1+i].type in BIN_OP_FUNCS:
f = BIN_OP_FUNCS[tokens1[i1+i].type]
if f(tokens1[i1].pattr, tokens1[i1+1].pattr) == tokens2[i2].pattr:
i1 += 3
i2 += 1
continue
elif tokens1[i1].kind == 'UNARY_NOT':
if tokens2[i2].kind == 'POP_JUMP_IF_TRUE':
if tokens1[i1+1].kind == 'POP_JUMP_IF_FALSE':
elif tokens1[i1].type == 'UNARY_NOT':
if tokens2[i2].type == 'POP_JUMP_IF_TRUE':
if tokens1[i1+1].type == 'POP_JUMP_IF_FALSE':
i1 += 2
i2 += 1
continue
elif tokens2[i2].kind == 'POP_JUMP_IF_FALSE':
if tokens1[i1+1].kind == 'POP_JUMP_IF_TRUE':
elif tokens2[i2].type == 'POP_JUMP_IF_FALSE':
if tokens1[i1+1].type == 'POP_JUMP_IF_TRUE':
i1 += 2
i2 += 1
continue
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') \
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') \
and int(tokens1[i1].offset) not in targets1:
i1 += 1
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' \
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' \
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].kind == 'LOAD_NAME' and tokens2[i2].kind == 'LOAD_CONST' \
elif tokens1[i1].type == 'LOAD_NAME' and tokens2[i2].type == 'LOAD_CONST' \
and tokens1[i1].pattr == 'None' and tokens2[i2].pattr is None:
pass
elif tokens1[i1].kind == 'LOAD_GLOBAL' and tokens2[i2].kind == 'LOAD_NAME' \
elif tokens1[i1].type == 'LOAD_GLOBAL' and tokens2[i2].type == 'LOAD_NAME' \
and tokens1[i1].pattr == tokens2[i2].pattr:
pass
elif tokens1[i1].kind == 'LOAD_ASSERT' and tokens2[i2].kind == 'LOAD_NAME' \
elif tokens1[i1].type == 'LOAD_ASSERT' and tokens2[i2].type == 'LOAD_NAME' \
and tokens1[i1].pattr == tokens2[i2].pattr:
pass
elif (tokens1[i1].kind == 'RETURN_VALUE' and
tokens2[i2].kind == 'RETURN_END_IF'):
elif (tokens1[i1].type == 'RETURN_VALUE' and
tokens2[i2].type == 'RETURN_END_IF'):
pass
elif (tokens1[i1].kind == 'BUILD_TUPLE_0' and
elif (tokens1[i1].type == 'BUILD_TUPLE_0' and
tokens2[i2].pattr == ()):
pass
else:
raise CmpErrorCode(name, tokens1[i1].offset, tokens1[i1],
tokens2[i2], tokens1, tokens2)
elif tokens1[i1].kind in JUMP_OPS and tokens1[i1].pattr != tokens2[i2].pattr:
if tokens1[i1].kind == 'JUMP_BACK':
elif tokens1[i1].type in JUMP_OPs and tokens1[i1].pattr != tokens2[i2].pattr:
if tokens1[i1].type == 'JUMP_BACK':
dest1 = int(tokens1[i1].pattr)
dest2 = int(tokens2[i2].pattr)
if offset_map[dest1] != dest2:
@@ -365,8 +363,10 @@ def cmp_code_objects(version, is_pypy, code_obj1, code_obj2,
elif member == 'co_flags':
flags1 = code_obj1.co_flags
flags2 = code_obj2.co_flags
if is_pypy:
if is_pypy or version == 2.4:
# For PYPY for now we don't care about PYPY_SOURCE_IS_UTF8:
# Python 2.4 also sets this flag and I am not sure
# where or why
flags2 &= ~0x0100 # PYPY_SOURCE_IS_UTF8
# We also don't care about COROUTINE or GENERATOR for now
flags1 &= ~0x000000a0
@@ -375,6 +375,8 @@ def cmp_code_objects(version, is_pypy, code_obj1, code_obj2,
raise CmpErrorMember(name, 'co_flags',
pretty_flags(flags1),
pretty_flags(flags2))
else:
# all other members must be equal
if getattr(code_obj1, member) != getattr(code_obj2, member):
@@ -385,28 +387,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.kind # shortcut
if t == 'BUILD_TUPLE_0' and o.kind == 'LOAD_CONST' and o.pattr == ():
t = self.type # shortcut
if t == 'BUILD_TUPLE_0' and o.type == 'LOAD_CONST' and o.pattr == ():
return 0
if t == 'COME_FROM' == o.kind:
if t == 'COME_FROM' == o.type:
return 0
if t == 'PRINT_ITEM_CONT' and o.kind == 'PRINT_ITEM':
if t == 'PRINT_ITEM_CONT' and o.type == 'PRINT_ITEM':
return 0
if t == 'RETURN_VALUE' and o.kind == 'RETURN_END_IF':
if t == 'RETURN_VALUE' and o.type == 'RETURN_END_IF':
return 0
if t == 'JUMP_IF_FALSE_OR_POP' and o.kind == 'POP_JUMP_IF_FALSE':
if t == 'JUMP_IF_FALSE_OR_POP' and o.type == '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.kind
return (t == o.kind) or self.pattr == o.pattr
return t == o.type
return (t == o.type) or self.pattr == o.pattr
def __repr__(self):
return '%s %s (%s)' % (str(self.kind), str(self.attr),
return '%s %s (%s)' % (str(self.type), str(self.attr),
repr(self.pattr))
def __str__(self):
return '%s\t%-17s %r' % (self.offset, self.kind, self.pattr)
return '%s\t%-17s %r' % (self.offset, self.type, self.pattr)
def compare_code_with_srcfile(pyc_filename, src_filename, weak_verify=False):
"""Compare a .pyc with a source code file."""
@@ -418,8 +420,10 @@ def compare_code_with_srcfile(pyc_filename, src_filename, weak_verify=False):
return msg
try:
code_obj2 = load_file(src_filename)
except SyntaxError as e:
except SyntaxError, e:
# src_filename can be the first of a group sometimes
if version == 2.4:
print(pyc_filename)
return str(e).replace(src_filename, pyc_filename)
cmp_code_objects(version, is_pypy, code_obj1, code_obj2, ignore_code=weak_verify)
return None
@@ -438,4 +442,4 @@ if __name__ == '__main__':
t2 = Token('LOAD_CONST', -421, 'code_object _expandLang', 55)
print(repr(t1))
print(repr(t2))
print(t1.kind == t2.kind, t1.attr == t2.attr)
print(t1.type == t2.type, t1.attr == t2.attr)

View File

@@ -1,3 +1,3 @@
# This file is suitable for sourcing inside bash as
# well as importing into Python
VERSION='2.13.1'
VERSION='2.11.0'