Compare commits

..

120 Commits

Author SHA1 Message Date
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
4a47822904 Fix more Python3.2 parser errors 2017-05-06 05:25:56 -04:00
rocky
4e9555a7f6 Improve Python 3.2 decompilation ...
by removing a lot of the control-flow labels of 3.3+
2017-05-05 21:34:21 -04:00
rocky
d1c0413b79 Try CI testing on Python 3.6 2017-05-05 17:55:01 -04:00
rocky
246495febd Bang more on BUIlD_MAP_UNPACK
there are still bugs. Note:

{**{'x': 1}, **{'y': 2}} and
{{'x': 1}, **{'y': 2}}

generate the same Python 3.5+ bytecode.
2017-05-02 21:55:41 -04:00
rocky
91b86ac156 BUILD_MAP_UNPACK'ing of dictionaries in 3.5 2017-05-02 05:51:48 -04:00
rocky
26cd91046e Remove extra unpack *. Issue #98 2017-05-01 05:26:55 -04:00
R. Bernstein
b42c66e091 Update HISTORY.md 2017-04-29 22:32:16 -04:00
rocky
364827a2f2 Handle BUILD_MAP_UNPACK in a build_list 2017-04-29 21:44:52 -04:00
rocky
819458564c A hacky way to get CALL_FUNCTION_EX_KW to work. 2017-04-27 21:38:30 -04:00
rocky
486f313532 remove debug code 2017-04-26 02:14:28 -04:00
rocky
84fd71b73b Python 3.6 CALL_FUNCTION_EX first attempt 2017-04-25 07:31:01 -04:00
rocky
50687e6317 Reduse scope of LOAD_ASSERT as expr to 3.4+ 2017-04-22 22:10:55 -04:00
rocky
b35546157f LOAD_ASSERT can also be an expr
This may have the undesirable property that assert statements might get
tagged with equivalant low-level Python code that uses "raise
AssertionError", but so be it.

Fixes #103
2017-04-22 20:03:21 -04:00
R. Bernstein
7755dddd94 Update HISTORY.md 2017-04-22 11:18:08 -04:00
R. Bernstein
ce1e841255 Update HISTORY.md 2017-04-22 11:15:45 -04:00
rocky
68f0f79030 History keeps gettting amended 2017-04-22 11:12:52 -04:00
rocky
bf195a234f Document Python 3.x status 2017-04-22 10:57:54 -04:00
rocky
87db833f62 Add await expr
Fixes #111
2017-04-22 10:39:20 -04:00
rocky
8081decf7c Update test 2017-04-22 04:29:18 -04:00
rocky
e5008693a1 3.3+ bug in handling single kwarg after *
Towards fixing issue #110
2017-04-22 04:19:04 -04:00
rocky
810649799c Add async for with pass statement
Fixes #109
2017-04-20 12:39:15 -04:00
rocky
d4be647bce 3.5 ifelsestmtl grammar bug.
Fixes #108
2017-04-19 05:08:48 -04:00
rocky
4a898ff4c1 Expand await stmt handling
Fixes #107
2017-04-18 01:51:23 -04:00
rocky
cb6925beec Add DELETE_DEREF grammar rule
Fixes Issue #106
2017-04-18 01:35:08 -04:00
rocky
2665f292c5 Rename test case to something more appropriate 2017-04-17 05:43:50 -04:00
rocky
33be34c6fb Fix botched test case
Thanks to Zm908 for pointing this out
2017-04-17 05:35:43 -04:00
rocky
3bbc94847d Comment on what's up with last change 2017-04-16 16:07:08 -04:00
rocky
3a8d4e1a12 Python 3.x ifelse in comprehension
Fixes Issue #91
2017-04-16 14:47:11 -04:00
rocky
87e005a7ba Add 2.7 complex test 2017-04-16 10:31:15 -04:00
rocky
5477ca294d Correct bug in 3.5+ build_list with UNPACK 2017-04-15 22:34:56 -04:00
R. Bernstein
31c28d0220 Update HOW-TO-REPORT-A-BUG.md 2017-04-15 18:56:56 -04:00
R. Bernstein
659e28d686 Update HOW-TO-REPORT-A-BUG.md 2017-04-15 18:42:01 -04:00
rocky
8a33a583cd 3.6 generates Wonky EXTENDED_ARG in expression
Fixes Issue #102
2017-04-15 18:31:39 -04:00
rocky
8a776176e2 Add how to report a bug
Add test case for ... if 1 else ...
2017-04-15 10:41:13 -04:00
rocky
03498963d4 Python 3.5+ BUILD_UNMAP_PACK rules
Towards addressing Issue #98
2017-04-14 23:39:56 -04:00
rocky
47dbc57f3d Reduce adding RETURN_END_IF in 3.5+
The whole control flow determination has to be redone in a less
haphazard way using real flow-control analysis. Hopefully that's on the
way.

In the meantime we have this hack.
2017-04-14 06:57:25 -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
39b9810587 Better names for a test 2017-04-14 05:05:02 -04:00
rocky
8cdaac93ab Add if1else. Fixes #101 2017-04-13 21:27:22 -04:00
rocky
a9f7a3c6d0 In 3.x come_from should include COME_FROM_EXCEPT 2017-04-13 20:27:02 -04:00
rocky
495bdd7b64 Towards fixing issue #92 2017-04-13 01:48:17 -04:00
rocky
b4ded92822 Add Python 2.3 rule for "if 1: ..."
Fully fixes #97 for Python 2.3. Python 2.4 was fixed in a previous commit.
2017-04-13 01:14:49 -04:00
rocky
be9194c223 annotate args type need to be expr's not constants 2017-04-12 20:12:41 -04:00
rocky
45bd8e4058 Handle Python 2.4 "if 1...." 2017-04-12 04:50:22 -04:00
rocky
bb24df596d Bang on 3.x annotations 2017-04-11 17:09:10 -04:00
rocky
6acec471e3 Towards fixing annotated decorator functions...
and annotate functions
2017-04-11 05:56:20 -04:00
rocky
41343c27b7 Misc bugs
parse2.py: restore accidently-removed while1stmt rule
scanner27.py: grammar typo
check_ast: add while1else to list of looping constructs
pysource.py: CALL_FUNCTION_VAR_KW_ARGS with positional args rule is different?
2017-04-10 07:57:56 -04:00
rocky
9e34654b38 Add more while1else grammar rules
Towards addressing issue #93
2017-04-10 02:47:46 -04:00
rocky
09eb7f7f78 Merge branch 'master' into python-2.4 2017-04-10 00:48:04 -04:00
rocky
b9703cf6b4 One more FUNCTION_VAR test for 3.3 2017-04-09 06:58:41 -04:00
rocky
792df2a7a7 Another Python 3.5 FUNCTION_VAR bug
Fixes #94
2017-04-09 06:54:32 -04:00
rocky
b4a6c3c319 Merge branch 'master' of github.com:rocky/python-uncompyle6 2017-04-09 05:32:46 -04:00
rocky
4199bc7f61 Fix Python 3.5 CALL_FUNCTION_VAR_KW
Fixes Issue #95
2017-04-09 05:30:45 -04:00
rocky
91e1d2538f Merge branch 'master' of github.com:rocky/python-uncompyle6 2017-04-03 06:53:32 -04:00
rocky
6773a66b99 Tidy use of load_attrs 2017-04-03 06:53:12 -04:00
rocky
ed6cb9af79 Merge branch 'master' of github.com:rocky/python-uncompyle6 2017-03-27 07:10:16 -04:00
rocky
a91cd71667 Note we've run this on Python 3.0-3.1 bytecodes
__pkginfo__.py: use ore recent xdis
2017-03-27 07:08:59 -04:00
rocky
6f82ae3642 Use more-recent xdis 2017-03-19 14:01:59 -04:00
rocky
4e05c741e3 grammar typo and add another test 2017-03-15 03:59:07 -04:00
rocky
fdcb90f661 Python 3.0 doesn't have POP_JUMP_IF... 2017-03-12 10:32:05 -04:00
rocky
f416473562 Note problem in handling pathologically long lists 2017-03-12 10:16:10 -04:00
rocky
5856802902 Small cleanup - remove POP_JUMP_TF 2017-03-07 22:07:29 -05:00
rocky
4f2ae2f603 More accurate ranges of try blocks in 3.x 2017-03-05 00:05:52 -05:00
rocky
ea1651d8ca More accurate ranges of try blocks in 3.x 2017-03-05 00:03:01 -05:00
R. Bernstein
be769da401 Merge pull request #84 from moagstar/property_based_test_function_call
Property based test function call
2017-03-04 14:43:34 -05:00
Daniel Bradburn
cb3c5e7119 validation now uses xdis for python2 support 2017-03-04 20:23:39 +01:00
rocky
39e3582e72 README updates for 3.5 and 1.5 2017-03-04 11:54:02 -05:00
rocky
a0c090932e Bug found by hypothesis in creating function calls 2017-03-04 11:49:09 -05:00
Daniel Bradburn
d1e118afa3 marked all function call tests as failing until they pass across all python versions 2017-03-04 13:04:31 +01:00
Daniel Bradburn
f7da8fd8ab added minimal examples for various function call opcodes 2017-03-04 12:44:11 +01:00
Daniel Bradburn
3b1dd9d1c4 added property based test for verifying uncompylation of function calls. A number of minimal examples for the various function call opcodes have been generated with the majority marked as expected failure until python 3.6 opcode support is complete. I'm hoping this will make it easier to figure out what needs to be done to support the new opcodes and changed semntics for function calls 2017-03-04 12:43:12 +01:00
Daniel Bradburn
91fd1ce732 reduced errors when generating function call instances 2017-03-03 21:38:53 +01:00
Daniel Bradburn
a46e7cbfa4 added test file for function calls 2017-03-03 21:06:31 +01:00
Daniel Bradburn
d46873c44d added .idea to gitignore 2017-03-03 21:03:50 +01:00
Daniel Bradburn
54e50771ab added .venv to gitignore 2017-03-03 21:03:06 +01:00
rocky
f7a910ec66 Merge branch 'master' into python-2.4 2017-03-01 05:55:26 -05:00
rocky
160ec0d9cc 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
2017-03-01 05:50:31 -05:00
rocky
e1111e3f50 Python 2.6 a == b or c == d == 3 grammar bug 2017-02-28 09:18:36 -05:00
rocky
65913778a5 2.6 a == b or x == y == z bug 2017-02-28 03:12:48 -05:00
rocky
cf21fff38b Predidence of cmp_list: x == y == z
The x, y, z should not have parenthesis around pairs of them
(x == y) or (y == z)
2017-02-28 01:25:33 -05:00
rocky
29122340e6 Python 2.7 check jump targets of "and" 2017-02-28 00:15:39 -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
114 changed files with 1685 additions and 568 deletions

2
.gitignore vendored
View File

@@ -16,3 +16,5 @@
/unpyc
__pycache__
build
/.venv*
/.idea

View File

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

593
ChangeLog
View File

@@ -1,11 +1,415 @@
2017-02-25 rocky <rb@dustyfeet.com>
2017-05-06 rocky <rb@dustyfeet.com>
* uncompyle6/version.py: Get ready for release 2.9.10
* uncompyle6/version.py: Get ready for release 2.9.11
2017-05-06 rocky <rb@dustyfeet.com>
* 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>
* uncompyle6/parsers/parse32.py, uncompyle6/scanners/scanner3.py:
Improve Python 3.2 decompilation ... by removing a lot of the control-flow labels of 3.3+
2017-05-05 rocky <rb@dustyfeet.com>
* .travis.yml: Try CI testing on Python 3.6
2017-05-02 rocky <rb@dustyfeet.com>
* test/simple_source/bug35/01_map_unpack.py, uncompyle6/parser.py,
uncompyle6/parsers/parse3.py, uncompyle6/parsers/parse35.py,
uncompyle6/semantics/pysource.py: Bang more on BUIlD_MAP_UNPACK there are still bugs. Note: {**{'x': 1}, **{'y': 2}} and {{'x': 1}, **{'y': 2}} generate the same Python 3.5+ bytecode.
2017-05-02 rocky <rb@dustyfeet.com>
* test/simple_source/bug35/01_map_unpack.py, uncompyle6/parser.py,
uncompyle6/parsers/parse3.py, uncompyle6/parsers/parse35.py:
BUILD_MAP_UNPACK'ing of dictionaries in 3.5
2017-05-01 rocky <rb@dustyfeet.com>
* uncompyle6/semantics/pysource.py: Remove extra unpack *. Issue #98
2017-04-29 R. Bernstein <rocky@users.noreply.github.com>
* HISTORY.md: Update HISTORY.md
2017-04-29 rocky <rb@dustyfeet.com>
* test/simple_source/bug35/01_map_unpack.py,
uncompyle6/parsers/parse35.py, uncompyle6/semantics/pysource.py:
Handle BUILD_MAP_UNPACK in a build_list
2017-04-27 rocky <rb@dustyfeet.com>
* uncompyle6/semantics/pysource.py: A hacky way to get
CALL_FUNCTION_EX_KW to work.
2017-04-26 rocky <rb@dustyfeet.com>
* uncompyle6/semantics/pysource.py: remove debug code
2017-04-25 rocky <rb@dustyfeet.com>
* test/simple_source/bug36/01_call_function.py,
uncompyle6/parsers/parse36.py, uncompyle6/scanners/scanner36.py,
uncompyle6/semantics/pysource.py: Python 3.6 CALL_FUNCTION_EX first
attempt
2017-04-22 rocky <rb@dustyfeet.com>
* uncompyle6/parser.py, uncompyle6/parsers/parse34.py: Reduse scope
of LOAD_ASSERT as expr to 3.4+
2017-04-22 rocky <rb@dustyfeet.com>
* uncompyle6/parser.py, uncompyle6/verify.py: LOAD_ASSERT can also
be an expr This may have the undesirable property that assert statements might
get tagged with equivalant low-level Python code that uses "raise
AssertionError", but so be it. Fixes #103
2017-04-22 R. Bernstein <rocky@users.noreply.github.com>
* HISTORY.md: Update HISTORY.md
2017-04-22 R. Bernstein <rocky@users.noreply.github.com>
* HISTORY.md: Update HISTORY.md
2017-04-22 rocky <rb@dustyfeet.com>
* HISTORY.md: History keeps gettting amended
2017-04-22 rocky <rb@dustyfeet.com>
* README.rst: Document Python 3.x status
2017-04-22 rocky <rb@dustyfeet.com>
* test/simple_source/bug35/03_async_await.py,
uncompyle6/parsers/parse35.py, uncompyle6/semantics/pysource.py: Add
await expr Fixes #111
2017-04-22 rocky <rb@dustyfeet.com>
* : Update test
2017-04-22 rocky <rb@dustyfeet.com>
* test/simple_source/bug33/02_pos_args.py,
uncompyle6/parsers/parse3.py, uncompyle6/semantics/make_function.py:
3.3+ bug in handling single kwarg after * Towards fixing issue #110
2017-04-20 rocky <rb@dustyfeet.com>
* test/simple_source/bug35/02_async_for.py,
uncompyle6/parsers/parse35.py: Add async for with pass statement Fixes #109
2017-04-19 rocky <rb@dustyfeet.com>
* test/simple_source/bug35/03_while-if-break.py,
uncompyle6/parsers/parse3.py: 3.5 ifelsestmtl grammar bug. Fixes #108
2017-04-18 rocky <rb@dustyfeet.com>
* test/simple_source/bug35/03_async_await.py,
uncompyle6/parsers/parse35.py: Expand await stmt handling Fixes #107
2017-04-18 rocky <rb@dustyfeet.com>
* test/simple_source/bug33/01_delete_deref.py,
uncompyle6/parsers/parse32.py, uncompyle6/semantics/pysource.py: Add
DELETE_DEREF grammar rule Fixes Issue #106
2017-04-17 rocky <rb@dustyfeet.com>
* test/simple_source/bug36/01_extended_arg.py,
test/simple_source/bug36/01_if_file.py: Rename test case to
something more appropriate
2017-04-17 rocky <rb@dustyfeet.com>
* test/simple_source/bug36/01_if_file.py: Fix botched test case Thanks to Zm908 for pointing this out
2017-04-16 rocky <rb@dustyfeet.com>
* uncompyle6/parsers/parse3.py: Comment on what's up with last
change
2017-04-16 rocky <rb@dustyfeet.com>
* test/simple_source/bug22/03_if1.py,
test/simple_source/bug31/02_ifelse_comprehension.py,
uncompyle6/parsers/parse3.py: Python 3.x ifelse in comprehension Fixes Issue #91
2017-04-16 rocky <rb@dustyfeet.com>
* : Add 2.7 complex test
2017-04-15 rocky <rb@dustyfeet.com>
* test/simple_source/bug35/01_map_unpack.py,
uncompyle6/semantics/pysource.py: Correct bug in 3.5+ build_list
with UNPACK
2017-04-15 R. Bernstein <rocky@users.noreply.github.com>
* HOW-TO-REPORT-A-BUG.md: Update HOW-TO-REPORT-A-BUG.md
2017-04-15 R. Bernstein <rocky@users.noreply.github.com>
* HOW-TO-REPORT-A-BUG.md: Update HOW-TO-REPORT-A-BUG.md
2017-04-15 rocky <rb@dustyfeet.com>
* test/simple_source/bug36/01_if_file.py,
uncompyle6/parsers/parse36.py: 3.6 generates Wonky EXTENDED_ARG in
expression Fixes Issue #102
2017-04-15 rocky <rb@dustyfeet.com>
* HOW-TO-REPORT-A-BUG.md, MANIFEST.in: Add how to report a bug Add test case for ... if 1 else ...
2017-04-14 rocky <rb@dustyfeet.com>
* test/simple_source/bug35/01_map_unpack.py,
uncompyle6/parsers/parse3.py, uncompyle6/parsers/parse35.py,
uncompyle6/semantics/pysource.py: Python 3.5+ BUILD_UNMAP_PACK rules Towards addressing Issue #98
2017-04-14 rocky <rb@dustyfeet.com>
* uncompyle6/scanners/scanner3.py: Reduce adding RETURN_END_IF in
3.5+ The whole control flow determination has to be redone in a less
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,
test/simple_source/bug27+/03_if_true_else.py: Better names for a
test
2017-04-13 rocky <rb@dustyfeet.com>
* test/simple_source/bug27+/03_if_true_else.py,
uncompyle6/parser.py, uncompyle6/parsers/parse3.py,
uncompyle6/semantics/consts.py: Add if1else. Fixes #101
2017-04-13 rocky <rb@dustyfeet.com>
* uncompyle6/parsers/parse3.py: In 3.x come_from should include
COME_FROM_EXCEPT
2017-04-13 rocky <rb@dustyfeet.com>
* uncompyle6/parsers/parse35.py: Towards fixing issue #92
2017-04-13 rocky <rb@dustyfeet.com>
* uncompyle6/parsers/parse23.py, uncompyle6/semantics/pysource.py:
Add Python 2.3 rule for "if 1: ..." Fully fixes #97 for Python 2.3. Python 2.4 was fixed in a previous
commit.
2017-04-12 rocky <rb@dustyfeet.com>
* uncompyle6/parsers/parse3.py,
uncompyle6/semantics/make_function.py: annotate args type need to be
expr's not constants
2017-04-12 rocky <rb@dustyfeet.com>
* uncompyle6/parsers/parse24.py: Handle Python 2.4 "if 1...."
2017-04-11 rocky <rb@dustyfeet.com>
* test/simple_source/bug31/04_def_annotate.py,
uncompyle6/semantics/fragments.py,
uncompyle6/semantics/make_function.py: Bang on 3.x annotations
2017-04-11 rocky <rb@dustyfeet.com>
* test/simple_source/bug31/04_def_annotate.py,
uncompyle6/parsers/parse3.py, uncompyle6/semantics/pysource.py:
Towards fixing annotated decorator functions... and annotate functions
2017-04-10 rocky <rb@dustyfeet.com>
* uncompyle6/parsers/parse2.py, uncompyle6/scanners/scanner27.py,
uncompyle6/semantics/check_ast.py, uncompyle6/semantics/pysource.py:
Misc bugs parse2.py: restore accidently-removed while1stmt rule scanner27.py:
grammar typo check_ast: add while1else to list of looping constructs
pysource.py: CALL_FUNCTION_VAR_KW_ARGS with positional args rule is
different?
2017-04-10 rocky <rb@dustyfeet.com>
* test/simple_source/stmts/02_while1else.py,
uncompyle6/parsers/parse2.py, uncompyle6/parsers/parse3.py,
uncompyle6/parsers/parse35.py: Add more while1else grammar rules Towards addressing issue #93
2017-04-10 rocky <rb@dustyfeet.com>
* : commit b9703cf6b41138b717c282fc791c08d807692b07 Author: rocky
<rb@dustyfeet.com> Date: Sun Apr 9 06:58:41 2017 -0400
2017-04-09 rocky <rb@dustyfeet.com>
* test/simple_source/def/10_kw+pos_args-bug.py,
uncompyle6/parsers/parse3.py, uncompyle6/semantics/pysource.py:
Another Python 3.5 FUNCTION_VAR bug Fixes #94
2017-04-09 rocky <rb@dustyfeet.com>
* : commit 4199bc7f617e387fb03fc06939cd17366dc15c5e Author: rocky
<rb@dustyfeet.com> Date: Sun Apr 9 05:30:45 2017 -0400
2017-04-03 rocky <rb@dustyfeet.com>
* : commit 6773a66b99d07e48290a77dbbbe3c71cc39c31ba Author: rocky
<rb@dustyfeet.com> Date: Mon Apr 3 06:53:12 2017 -0400
2017-03-27 rocky <rb@dustyfeet.com>
* : commit a91cd716670be09d3cef34e1bb36a67f96f91712 Author: rocky
<rb@dustyfeet.com> Date: Mon Mar 27 07:08:59 2017 -0400
2017-03-19 rocky <rb@dustyfeet.com>
* __pkginfo__.py: Use more-recent xdis
2017-03-15 rocky <rb@dustyfeet.com>
* HISTORY.md, test/simple_source/bug33/01_if_try_except.py: grammar
typo and add another test
2017-03-12 rocky <rb@dustyfeet.com>
* uncompyle6/scanners/scanner3.py: Python 3.0 doesn't have
POP_JUMP_IF...
2017-03-12 rocky <rb@dustyfeet.com>
* README.rst: Note problem in handling pathologically long lists
2017-03-07 rocky <rb@dustyfeet.com>
* uncompyle6/scanners/scanner3.py: Small cleanup - remove
POP_JUMP_TF
2017-03-05 rocky <rb@dustyfeet.com>
* pytest/test_grammar.py, uncompyle6/parsers/parse3.py,
uncompyle6/parsers/parse33.py, uncompyle6/scanners/scanner3.py: More
accurate ranges of try blocks in 3.x
2017-03-05 rocky <rb@dustyfeet.com>
* test/simple_source/bug33/01_try_except.py: More accurate ranges of
try blocks in 3.x
2017-03-04 R. Bernstein <rocky@users.noreply.github.com>
* : Merge pull request #84 from
moagstar/property_based_test_function_call Property based test function call
2017-03-04 rocky <rb@dustyfeet.com>
* README.rst: README updates for 3.5 and 1.5
2017-03-04 rocky <rb@dustyfeet.com>
* test/simple_source/bug32/01_named_and_kwargs.py,
uncompyle6/parsers/parse3.py: Bug found by hypothesis in creating
function calls
2017-03-04 Daniel Bradburn <moagstar@gmail.com>
* pytest/test_function_call.py: marked all function call tests as
failing until they pass across all python versions
2017-03-04 Daniel Bradburn <moagstar@gmail.com>
* pytest/test_function_call.py: added minimal examples for various
function call opcodes
2017-03-04 Daniel Bradburn <moagstar@gmail.com>
* pytest/test_function_call.py: added property based test for
verifying uncompylation of function calls. A number of minimal
examples for the various function call opcodes have been generated
with the majority marked as expected failure until python 3.6 opcode
support is complete. I'm hoping this will make it easier to figure
out what needs to be done to support the new opcodes and changed
semntics for function calls
2017-03-03 Daniel Bradburn <moagstar@gmail.com>
* pytest/test_function_call.py: reduced errors when generating
function call instances
2017-03-03 Daniel Bradburn <moagstar@gmail.com>
* pytest/test_function_call.py: added test file for function calls
2017-03-03 Daniel Bradburn <moagstar@gmail.com>
* .gitignore: added .idea to gitignore
2017-03-03 Daniel Bradburn <moagstar@gmail.com>
* .gitignore: added .venv to gitignore
2017-03-01 rocky <rb@dustyfeet.com>
* : commit 160ec0d9cc5fe347f6e8bdb69515a28c76cfb368 Author: rocky
<rb@dustyfeet.com> Date: Wed Mar 1 05:50:31 2017 -0500
2017-02-28 rocky <rb@dustyfeet.com>
* README.rst, uncompyle6/parser.py, uncompyle6/parsers/parse26.py:
Python 2.6 a == b or c == d == 3 grammar bug
2017-02-28 rocky <rb@dustyfeet.com>
* : 2.6 a == b or x == y == z bug
2017-02-28 rocky <rb@dustyfeet.com>
* test/simple_source/bug26/03_double_equals.py,
uncompyle6/semantics/consts.py: Predidence of cmp_list: x == y == z The x, y, z should not have parenthesis around pairs of them (x ==
y) or (y == z)
2017-02-28 rocky <rb@dustyfeet.com>
* uncompyle6/parser.py, uncompyle6/parsers/parse27.py: Python 2.7
check jump targets of "and"
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
* : 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: 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>
@@ -19,9 +423,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>
@@ -60,6 +470,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,
@@ -67,9 +481,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>
@@ -173,6 +602,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
@@ -182,9 +615,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>
@@ -283,6 +729,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
@@ -297,6 +753,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
@@ -306,6 +770,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
@@ -334,10 +805,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>
@@ -364,8 +834,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>
@@ -385,6 +854,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
@@ -397,15 +871,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>
@@ -426,22 +904,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>
@@ -509,15 +999,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>
@@ -568,11 +1055,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,
@@ -584,8 +1094,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,7 +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 could verify against the entire Python library.
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
@@ -65,10 +66,12 @@ 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 for more details here. There wasn't a public
[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
python library.
Python library. But given subsequent bugs found like simply
recognizing complex-number constants in bytecode, decompilation wasn't perfect.
Next we get to ["uncompyle" and
PyPI](https://pypi.python.org/pypi/uncompyle/1.1) and the era of
@@ -95,17 +98,17 @@ so. Then hamled made a few commits earler on, while Eike Siewertsen
made a few commits later on. But mostly wibiti, and Guenther
Starnberger got the code to where uncompyle2 was around 2012.
In uncompyle2 decompilation of python bytecode 2.5 & 2.6 is done by
In `uncompyle`, decompilation of python bytecode 2.5 & 2.6 is done by
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
This project, `uncompyle6`, abandons that approach for various
reasons. However the main reason is that we need offsets in fragment
deparsing to be exactly the same, and the transformation process can
remove instructions. Adding instructions with psuedo_offsets is
remove instructions. _Adding_ instructions with psuedo offsets is
however okay.
Uncompyle6, however owes its existence to the fork of uncompyle2 by
`Uncompyle6` however owes its existence to the fork of `uncompyle2` by
Myst herie (Mysterie) whose first commit picks up at
2012. I chose this since it seemed to have been at that time the most
actively, if briefly, worked on. Also starting around 2012 is Dark
@@ -115,9 +118,12 @@ I started working on this late 2015, mostly to add fragment support.
In that, I decided to make this runnable on Python 3.2+ and Python 2.6+
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: load loading and disassembly (xdis), parsing and tree
building (spark_parser), and grammar and semantic actions for
decompiling (uncompyle6).
projects:
* 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/spark_parser)).
Over the many years, code styles and Python features have
@@ -142,16 +148,19 @@ if the grammar is LR or left recursive.
Another approach that doesn't use grammars is to do something like
simulate execution symbolically and build expression trees off of
stack results. The two important projects that work this way are
[unpyc3](https://code.google.com/p/unpyc3/) and most especially
[pycdc](https://github.com/zrax/pycdc) The latter project is largely
by Michael Hansen and Darryl Pogue. If they supported getting
source-code fragments and I could call it from Python, I'd probably
ditch this and use that. From what I've seen, the code runs blindingly
fast and spans all versions of Python.
stack results. Control flow in that apprproach still needs to be
handled somewhat ad hoc. The two important projects that work this
way are [unpyc3](https://code.google.com/p/unpyc3/) and most
especially [pycdc](https://github.com/zrax/pycdc) The latter project
is largely by Michael Hansen and Darryl Pogue. If they supported
getting source-code fragments, did a better job in supporting Python
more fully, and had a way I could call it from Python, I'd probably
would have ditched this and used that. The code runs blindingly fast
and spans all versions of Python, although more recently Python 3
support has been lagging.
Tests for the project have been, or are being, culled from all of the
projects mentioned.
NB. If you find mistakes, want corrections, or want your name added (or removed),
please contact me.
NB. If you find mistakes, want corrections, or want your name added
(or removed), please contact me.

63
HOW-TO-REPORT-A-BUG.md Normal file
View File

@@ -0,0 +1,63 @@
# How to report a Bug
## 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.
I have found bugs in *every* Python decompiler I have tried. Even
those where authors/maintainers claim that they have used it on
the entire Python standard library. And I don't mean that
the program doesn't come out with the same Python source instructions,
but that the program is *semantically* not equivalent.
So it is likely you'll find a mistranslation in decompiling.
## What to send (minimum requirements)
The basic requirement is pretty simple:
* Python bytecode
* Source text
## What to send (additional helpful information)
Some kind folks also give the invocation they used and the output
which usually includes an error message produced. This is helpful. I
can figure out what OS you are running this on and what version of
*uncomplye6* was used. Therefore, if you don't provide the input
command and the output from that, please give:
* _uncompile6_ version used
* OS that you used this on
* Python interpreter version used
### But I don't *have* the source code!
Sure, I get it. No problem. There is Python assembly code on parse
errors, so simply by hand decompile that. To get a full disassembly, use pydisasm from the [xdis](https://pypi.python.org/pypi/xdis) package. Opcodes are described in the documentation for the [dis](https://docs.python.org/3.6/library/dis.html) module.
### But I don't *have* the source code and am incapable of figuring how how to do a hand disassembly!
Well, you could learn. No one is born into this world knowing how to disassemble Python bytecode. And as Richard Feynman once said, "What one fool can learn, so can another."
## Narrowing the problem
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.
Python modules can get quite large, and usually decompilation problems
occur in a single function or maybe the main-line code but not any of
the functions or classes. So please chop down the source code by
removing those parts that do to decompile properly.
By doing this, you'll probably have a better sense of what exactly is
the problem. Perhaps you can find the boundary of what decompiles, and
what doesn't. That is useful. Or maybe the same file will decompile
properly on a neighboring version of Python. That is helpful too.
In sum, the more you can isolate or narrow the problem, the more
likley the problem will be fixed and fixed sooner.

View File

@@ -1,6 +1,7 @@
include README.rst
include ChangeLog
include HISTORY.md
include HOW-TO-REPORT-A-BUG.md
include LICENSE
include Makefile
include requirements.txt

View File

@@ -37,7 +37,7 @@ check-3.0 check-3.1 check-3.2 check-3.5 check-3.6:
$(MAKE) -C test $@
#: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
@@ -59,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:

6
NEWS
View File

@@ -1,3 +1,9 @@
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 2016-02-25
- Python grammar rule fixes

View File

@@ -11,8 +11,8 @@ Introduction
------------
*uncompyle6* translates Python bytecode back into equivalent Python
source code. It accepts bytecodes from Python version 2.1 to 3.6 or
so, including PyPy bytecode and Dropbox's Python 2.5 bytecode.
source code. It accepts bytecodes from Python version 1.5, and 2.1 to
3.6 or so, including PyPy bytecode and Dropbox's Python 2.5 bytecode.
Why this?
---------
@@ -21,7 +21,8 @@ There were a number of decompyle, uncompile, uncompyle2, uncompyle3
forks around. All of them came basically from the same code base, and
almost all of them no were no longer actively maintained. Only one
handled Python 3, and even there, only 3.2 or 3.3 depending on which
code is used. This code pulls these together and moves forward. It
code is used. This code pulls these together and moves forward. This
project has the most complete support for Python 3.3 and above. It
also addresses a number of open issues in the previous forks.
What makes this different from other CPython bytecode decompilers?: its
@@ -46,7 +47,7 @@ Requirements
This project requires Python 2.6 or later, PyPy 3-2.4, or PyPy-5.0.1.
Python versions 2.4-2.7 are supported in the python-2.4 branch.
The bytecode files it can read has been tested on Python bytecodes from
versions 1.5, 2.1-2.7, and 3.2-3.6 and the above-mentioned PyPy versions.
versions 1.5, 2.1-2.7, and 3.0-3.6 and the above-mentioned PyPy versions.
Installation
------------
@@ -112,7 +113,7 @@ with handling control flow. All of the Python decompilers I have looked
at have the same problem. In some cases we can detect an erroneous
decompilation and report that.
About 90% of the decompilation of Python standard library packages in
Over 98% of the decompilation of Python standard library packages in
Python 2.7.12 verifies correctly. Over 99% of Python 2.7 and 3.3-3.5
"weakly" verify. Python 2.6 drops down to 96% weakly verifying.
Other versions drop off in quality too.
@@ -140,11 +141,10 @@ and 2.0.
In the Python 3 series, Python support is is strongest around 3.4 or
3.3 and drops off as you move further away from those versions. Python
3.5 largely works, but still has some bugs in it and is missing some
opcodes. Python 3.6 changes things drastically by using word codes
rather than byte codes. That has been addressed, but then it also
changes function call opcodes and its semantics and has more problems
with control flow than 3.5 has.
3.6 changes things drastically by using word codes rather than byte
codes. That has been addressed, but then it also changes function call
opcodes and its semantics and has more problems with control flow than
3.5 has.
Currently not all Python magic numbers are supported. Specifically in
some versions of Python, notably Python 3.6, the magic number has
@@ -158,13 +158,16 @@ We also don't handle PJOrion_ obfuscated code. For that try: PJOrion
Deobfuscator_ to unscramble the bytecode to get valid bytecode before
trying this tool.
Handling pathologically long lists of expressions or statements is
slow.
There is lots to do, so please dig in and help.
See Also
--------
* https://github.com/zrax/pycdc : supports all versions of Python and is written in C++
* https://github.com/zrax/pycdc : supports all versions of Python and is written in C++. Support for later Python 3 versions is a bit lacking though.
* https://code.google.com/archive/p/unpyc3/ : supports Python 3.2 only. The above projects use a different decompiling technique 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.

View File

@@ -40,7 +40,7 @@ entry_points={
]}
ftp_url = None
install_requires = ['spark-parser >= 1.6.0, < 1.7.0',
'xdis >= 3.2.4, < 3.3.0']
'xdis >= 3.3.0, < 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,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

@@ -0,0 +1,128 @@
# std
import string
# 3rd party
from hypothesis import given, assume, strategies as st
import pytest
# uncompyle
from validate import validate_uncompyle
alpha = st.sampled_from(string.ascii_lowercase)
numbers = st.sampled_from(string.digits)
alphanum = st.sampled_from(string.ascii_lowercase + string.digits)
expressions = st.sampled_from([x for x in string.ascii_lowercase + string.digits] + ['x+1'])
@st.composite
def function_calls(draw):
"""
Strategy factory for generating function calls.
:param draw: Callable which draws examples from other strategies.
:return: The function call text.
"""
list1 = st.lists(alpha, min_size=0, max_size=1)
list3 = st.lists(alpha, min_size=0, max_size=3)
positional_args = draw(list3)
named_args = [x + '=0' for x in draw(list3)]
star_args = ['*' + x for x in draw(list1)]
double_star_args = ['**' + x for x in draw(list1)]
arguments = positional_args + named_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, named,
# 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
@pytest.mark.xfail()
def test_CALL_FUNCTION():
validate_uncompyle("fn(w,m,f)")
@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_KW():
validate_uncompyle("fn(j=0)")
@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)
examples = set()
generate_examples = False
@pytest.mark.skipif(not generate_examples, reason='not generating examples')
@given(function_calls())
def test_generate_hypothesis(function_call):
examples.add(function_call)
@pytest.mark.skipif(not generate_examples, reason='not generating examples')
def test_generate_examples():
import dis
example_opcodes = {}
for example in examples:
opcodes = tuple(sorted(set(
instruction.opname
for instruction in dis.Bytecode(example)
if instruction.opname not in ('LOAD_CONST', 'LOAD_NAME', 'RETURN_VALUE')
)))
example_opcodes[opcodes] = example
for k, v in example_opcodes.items():
print('def test_' + '_'.join(k) + '():\n validate_uncompyle("' + v + '")\n\n')
return

View File

@@ -40,7 +40,9 @@ def test_grammar():
ignore_set = set(
"""
JUMP_BACK CONTINUE RETURN_END_IF
COME_FROM COME_FROM_EXCEPT COME_FROM_LOOP COME_FROM_WITH
COME_FROM COME_FROM_EXCEPT
COME_FROM_EXCEPT_CLAUSE
COME_FROM_LOOP COME_FROM_WITH
COME_FROM_FINALLY ELSE
LOAD_GENEXPR LOAD_ASSERT LOAD_SETCOMP LOAD_DICTCOMP
LAMBDA_MARKER RETURN_LAST

View File

@@ -2,18 +2,23 @@
from __future__ import print_function
# std
import os
import dis
import difflib
import subprocess
import tempfile
import functools
# compatability
import six
# uncompyle6 / xdis
from uncompyle6 import PYTHON_VERSION, deparse_code
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
from xdis.bytecode import Bytecode
from xdis.main import get_opcode
opc = get_opcode(PYTHON_VERSION, IS_PYPY)
Bytecode = functools.partial(Bytecode, opc=opc)
def _dis_to_text(co):
return dis.Bytecode(co).dis()
return Bytecode(co).dis()
def print_diff(original, uncompyled):
@@ -99,9 +104,8 @@ def are_code_objects_equal(co1, co2):
:return: True if the two code objects are approximately equal, otherwise False.
"""
# TODO : Use xdis for python2 compatability
instructions1 = dis.Bytecode(co1)
instructions2 = dis.Bytecode(co2)
instructions1 = Bytecode(co1)
instructions2 = Bytecode(co2)
for opcode1, opcode2 in zip(instructions1, instructions2):
if not are_instructions_equal(opcode1, opcode2):
return False

View File

@@ -3,7 +3,7 @@ PHONY=check clean dist distclean test test-unit test-functional rmChangeLog clea
GIT2CL ?= git2cl
PYTHON ?= python
PYTHON_VERSION = $(shell $(PYTHON) -V | cut -d ' ' -f 2 | cut -d'.' -f1,2)
PYTHON_VERSION = $(shell $(PYTHON) -V 2>&1 | cut -d ' ' -f 2 | cut -d'.' -f1,2)
NATIVE_CHECK = check-$(PYTHON_VERSION)
# Set COMPILE='--compile' to force compilation before check
@@ -20,7 +20,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-2.7-ok
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
@@ -68,7 +68,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
@@ -98,29 +98,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
@@ -149,9 +126,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.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
test/bytecode_3.2/03_if.pyc Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,7 @@
# From https://github.com/ToontownInfinite /src/otp/avatar/LocalAvatar.py#L364
if 1:
def jumpLandAnimFix(self, jumpTime):
return 5
def jumpLand(self):
return 6

View File

@@ -0,0 +1,6 @@
# From Python 2.7 parse_starttag HTMLParser.pyc
attrvalue = [1,2]
while attrvalue:
if attrvalue[:1] == 5 or \
attrvalue[:1] == 2 == attrvalue[-1:]:
attrvalue = 10

View File

@@ -0,0 +1 @@
1 if 1 else __file__

View File

@@ -0,0 +1,12 @@
# Python 2.7 sqlalchemy-1.013/sql/crud.py
def _extend_values_for_multiparams(compiler, stmt, c):
c(
[
(
(compiler() if compiler()
else compiler())
if c in stmt else compiler(),
)
]
for i in enumerate(stmt)
)

View File

@@ -17,3 +17,14 @@ def div(a: dict(type=float, help='the dividend'),
) -> dict(type=float, help='the result of dividing a by b'):
"""Divide a by b"""
return a / b
class TestSignatureObject(unittest.TestCase):
def test_signature_on_wkwonly(self):
def test(*, a:float, b:str) -> int:
pass
class SupportsInt(_Protocol):
@abstractmethod
def __int__(self) -> int:
pass

View File

@@ -18,3 +18,12 @@ def __init__(self, defaults=None, dict_type=_default_dict,
default_section=DEFAULTSECT,
interpolation=_UNSET):
pass
# Bug found by hypothesis in creating function calls
# thanks to moagstar
def fn(a, b, d):
return (a, b, d)
b = {'b': 1,
'd': 2}
fn(a=0, **b)

View File

@@ -0,0 +1,9 @@
# From 3.2 _abcoll.py
def pop(self):
it = iter(self)
try:
value = next(it)
except StopIteration:
raise KeyError
self.discard(value)
return value

View File

@@ -0,0 +1,11 @@
# From 3.2 shlex.py
def _samefile(os, src, dst):
if hasattr(os.path, 'samefile'):
try:
return os.path.samefile(src, dst)
except OSError:
return False
# All other platforms: check for same pathname.
return (os.path.normcase(os.path.abspath(src)) ==
os.path.normcase(os.path.abspath(dst)))

View File

@@ -0,0 +1,4 @@
def a():
del y
def b():
return y

View File

@@ -0,0 +1,16 @@
# From 3.3.5 _osx_support.py
def _get_system_version():
if __file__ is None:
try:
m = 5
except IOError:
pass
else:
try:
m = 10
finally:
m = 15
if m is not None:
m = 20
return m

View File

@@ -0,0 +1,16 @@
# From 3.3.5 _osx_support.py
def _get_system_version():
if __file__ is None:
try:
m = 5
except IOError:
pass
else:
try:
m = 10
finally:
m = 15
if m is not None:
m = 20
return m

View File

@@ -1,3 +1,6 @@
# From Python 3.3.6 hmac.py
# Problem was getting wrong placement of positional args
digest_cons = lambda d=b'': 5
# Handle single kwarg
lambda *, d=0: None

View File

@@ -0,0 +1,9 @@
# Python 3.5+ PEP 448 - Additional Unpacking Generalizations for dictionaries
{**{}}
{**{'a': 1, 'b': 2}}
## {**{'x': 1}, **{'y': 2}}
# {'c': 1, {'d': 2}, **{'e': 3}}
[*[]]
{**{0:0 for a in b}}
## {**{}, **{}}
## {**{}, **{}, **{}}

View File

@@ -0,0 +1,3 @@
async def a(b, c):
async for b in c:
pass

View File

@@ -24,3 +24,9 @@ async def awith_test():
async def awith_as_test():
async with 1 as i:
print(i)
async def f(z):
await z
async def g(z):
return await z

View File

@@ -5,3 +5,10 @@ def display_date(loop):
if loop.time():
break
x = 5
# Another loop to test 3.5 ifelsestmtl grammar rule
while loop:
if x:
True
else:
True

View File

@@ -0,0 +1,5 @@
# Python 3.6's changes for calling functions.
# See https://github.com/rocky/python-uncompyle6/issues/58
# CALL_FUNCTION_EX takes 2 to 3 arguments on the stack: the function, the tuple of positional arguments,
# and optionally the dict of keyword arguments if bit 0 of oparg is 1.
a(*[])

View File

@@ -0,0 +1,2 @@
if __file__:
0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0

View File

@@ -8,3 +8,20 @@ def __init__(self, defaults=None, dict_type=_default_dict,
default_section=DEFAULTSECT,
interpolation=_UNSET):
pass
# From 3.5 sqlalchemy/orm/__init__.py
# Python 3.5 changes the stack position of where * args are (furthest down the stack)
# Python 3.6+ replaces CALL_FUNCTION_VAR_KW with CALL_FUNCTION_EX
def deferred(*columns, **kw):
return ColumnProperty(deferred=True, *columns, **kw)
# From sqlalchemy/sql/selectable.py
class GenerativeSelect():
def __init__(self,
ClauseList,
util,
order_by=None):
self._order_by_clause = ClauseList(
*util.to_list(order_by),
_literal_as_text=5)

View File

@@ -0,0 +1,8 @@
# From Python-3.5.2/Lib/multiprocessing/connection.py
def PipeClient(address):
while 1:
z = 2
else:
raise
return

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
from fnmatch import fnmatch

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,9 +161,9 @@ 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, [],
@@ -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
@@ -127,7 +131,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())
@@ -144,9 +151,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:
@@ -180,31 +187,35 @@ def main(in_base, out_base, files, codes, outfile=None,
msg = verify.compare_code_with_srcfile(infile, outfile, weak_verify=weak_verify)
if not 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(outfile, 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
if not outfile:
mess = '\n# okay decompiling'
# mem_usage = __memUsage()
print(mess, infile)
print mess, infile
if outfile:
sys.stdout.write("%s\r" %
status_msg(do_verify, tot_files, okay_files, failed_files, verify_failed_files))

View File

@@ -6,8 +6,6 @@
Common uncompyle parser routines.
"""
from __future__ import print_function
import sys
from xdis.code import iscode
@@ -30,13 +28,19 @@ class PythonParser(GenericASTBuilder):
def __init__(self, AST, start, debug):
super(PythonParser, self).__init__(AST, start, debug)
self.collect = frozenset(
['stmts', 'except_stmts', '_stmts',
'exprlist', 'kvlist', 'kwargs', 'come_froms',
# 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'):
return ast.offset
else:
return self.ast_first_offset(ast[0])
def add_unique_rule(self, rule, opname, count, customize):
"""Add rule to grammar, but only if it hasn't been added previously
@@ -86,7 +90,10 @@ class PythonParser(GenericASTBuilder):
def fix(c):
s = str(c)
i = s.find('_')
return s if i == -1 else s[:i]
if i == -1:
return s
else:
return s[:i]
prefix = ''
if parent and tokens:
@@ -117,7 +124,10 @@ 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)
@@ -395,8 +405,7 @@ class PythonParser(GenericASTBuilder):
import_cont ::= LOAD_CONST LOAD_CONST import_as_cont
import_as_cont ::= IMPORT_FROM designator
load_attrs ::= LOAD_ATTR
load_attrs ::= load_attrs LOAD_ATTR
load_attrs ::= LOAD_ATTR+
"""
def p_list_comprehension(self, args):
@@ -503,8 +512,12 @@ class PythonParser(GenericASTBuilder):
expr ::= conditional
conditional ::= expr jmp_false expr JUMP_FORWARD expr COME_FROM
conditional ::= expr jmp_false expr JUMP_ABSOLUTE expr
expr ::= conditionalnot
conditionalnot ::= expr jmp_true expr _jump expr COME_FROM
conditionalnot ::= expr jmp_true expr _jump expr COME_FROM
expr ::= conditionalTrue
conditionalTrue ::= expr JUMP_FORWARD expr COME_FROM
ret_expr ::= expr
ret_expr ::= ret_and

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
@@ -44,7 +42,8 @@ class Python2Parser(PythonParser):
while1stmt ::= SETUP_LOOP l_stmts JUMP_BACK COME_FROM
while1stmt ::= SETUP_LOOP l_stmts JUMP_BACK POP_BLOCK COME_FROM
while1elsestmt ::= SETUP_LOOP l_stmts JUMP_BACK else_suite COME_FROM
while1elsestmt ::= SETUP_LOOP l_stmts JUMP_BACK POP_BLOCK else_suite COME_FROM
while1elsestmt ::= SETUP_LOOP l_stmts JUMP_BACK else_suite COME_FROM
exec_stmt ::= expr exprlist DUP_TOP EXEC_STMT
exec_stmt ::= expr exprlist EXEC_STMT

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2016 Rocky Bernstein
# Copyright (c) 2016-2017 Rocky Bernstein
# Copyright (c) 2000-2002 by hartmut Goebel <hartmut@goebel.noris.de>
# Copyright (c) 1999 John Aycock
@@ -14,6 +14,17 @@ class Python23Parser(Python24Parser):
def p_misc23(self, args):
'''
# Python 2.4 only adds something like the below for if 1:
# However we will just treat it as a noop (which of course messes up
# simple verify of bytecode.
# See also below in reduce_is_invalid where we check that the JUMP_FORWARD
# target matches the COME_FROM target
stmt ::= if1_stmt
if1_stmt ::= JUMP_FORWARD JUMP_IF_FALSE THEN POP_TOP COME_FROM
stmts
JUMP_FORWARD COME_FROM POP_TOP COME_FROM
# Used to keep semantic positions the same across later versions
# of Python
_while1test ::= SETUP_LOOP JUMP_FORWARD JUMP_IF_FALSE POP_TOP COME_FROM
@@ -33,6 +44,23 @@ class Python23Parser(Python24Parser):
lc_body ::= LOAD_FAST expr LIST_APPEND
'''
def add_custom_rules(self, tokens, customize):
super(Python23Parser, self).add_custom_rules(tokens, customize)
def reduce_is_invalid(self, rule, ast, tokens, first, last):
invalid = super(Python24Parser,
self).reduce_is_invalid(rule, ast,
tokens, first, last)
if invalid:
return invalid
# FiXME: this code never gets called...
lhs = rule[0]
if lhs == 'nop_stmt':
return not int(tokens[first].pattr) == tokens[last].offset
return False
class Python23ParserSingle(Python23Parser, PythonParserSingle):
pass

View File

@@ -14,6 +14,15 @@ class Python24Parser(Python25Parser):
def p_misc24(self, args):
'''
# Python 2.4 only adds something like the below for if 1:
# However we will just treat it as a noop (which of course messes up
# simple verify of bytecode.
# See also below in reduce_is_invalid where we check that the JUMP_FORWARD
# target matches the COME_FROM target
stmt ::= nop_stmt
nop_stmt ::= JUMP_FORWARD POP_TOP COME_FROM
# 2.5+ has two LOAD_CONSTs, one for the number '.'s in a relative import
# keep positions similar to simplify semantic actions
@@ -37,6 +46,25 @@ class Python24Parser(Python25Parser):
gen_comp_body ::= expr YIELD_VALUE
'''
def add_custom_rules(self, tokens, customize):
super(Python24Parser, self).add_custom_rules(tokens, customize)
if self.version == 2.4:
self.check_reduce['nop_stmt'] = 'tokens'
def reduce_is_invalid(self, rule, ast, tokens, first, last):
invalid = super(Python24Parser,
self).reduce_is_invalid(rule, ast,
tokens, first, last)
if invalid:
return invalid
# FiXME: this code never gets called...
lhs = rule[0]
if lhs == 'nop_stmt':
return not int(tokens[first].pattr) == tokens[last].offset
return False
class Python24ParserSingle(Python24Parser, PythonParserSingle):
pass

View File

@@ -157,7 +157,6 @@ class Python26Parser(Python2Parser):
# Semantic actions want the else to be at position 3
ifelsestmt ::= testexpr c_stmts_opt jf_cf_pop else_suite come_froms
ifelsestmt ::= testexpr_then c_stmts_opt jf_cf_pop else_suite come_froms
ifelsestmt ::= testexpr c_stmts_opt filler else_suitel come_froms POP_TOP
ifelsestmt ::= testexpr_then c_stmts_opt filler else_suitel come_froms POP_TOP
# Semantic actions want else_suitel to be at index 3

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2016 Rocky Bernstein
# Copyright (c) 2016-2017 Rocky Bernstein
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
# Copyright (c) 2000-2002 by hartmut Goebel <hartmut@goebel.noris.de>
@@ -96,6 +96,27 @@ class Python27Parser(Python2Parser):
while1stmt ::= SETUP_LOOP return_stmts COME_FROM
"""
def add_custom_rules(self, tokens, customize):
super(Python27Parser, self).add_custom_rules(tokens, customize)
self.check_reduce['and'] = 'AST'
return
def reduce_is_invalid(self, rule, ast, tokens, first, last):
invalid = super(Python27Parser,
self).reduce_is_invalid(rule, ast,
tokens, first, last)
if invalid:
return invalid
if rule == ('and', ('expr', 'jmp_false', 'expr', '\\e_come_from_opt')):
# Test that jmp_false jumps to the end of "and"
# or that it jumps to the same place as the end of "and"
jmp_false = ast[1][0]
jmp_target = jmp_false.offset + jmp_false.attr + 3
return not (jmp_target == tokens[last].offset or
tokens[last].pattr == jmp_false.pattr)
return False
class Python27ParserSingle(Python27Parser, PythonParserSingle):
pass

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2015, 2016 Rocky Bernstein
# Copyright (c) 2015-2017 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
@@ -15,8 +15,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
@@ -149,23 +147,25 @@ class Python3Parser(PythonParser):
iflaststmtl ::= testexpr c_stmts_opt JUMP_BACK COME_FROM_LOOP
# These are used to keep AST indices the same
jf_else ::= JUMP_FORWARD ELSE
ja_else ::= JUMP_ABSOLUTE ELSE
jump_forward_else ::= JUMP_FORWARD ELSE
jump_absolute_else ::= JUMP_ABSOLUTE ELSE
# Note: in if/else kinds of statements, we err on the side
# of missing "else" clauses. Therefore we include grammar
# rules with and without ELSE.
ifelsestmt ::= testexpr c_stmts_opt JUMP_FORWARD else_suite COME_FROM
ifelsestmt ::= testexpr c_stmts_opt jf_else else_suite _come_from
ifelsestmt ::= testexpr c_stmts_opt JUMP_FORWARD else_suite opt_come_from_except
ifelsestmt ::= testexpr c_stmts_opt jump_forward_else else_suite _come_from
ifelsestmtc ::= testexpr c_stmts_opt JUMP_ABSOLUTE else_suitec
ifelsestmtc ::= testexpr c_stmts_opt ja_else else_suitec
ifelsestmtc ::= testexpr c_stmts_opt jump_absolute_else else_suitec
ifelsestmtr ::= testexpr return_if_stmts return_stmts
ifelsestmtl ::= testexpr c_stmts_opt JUMP_BACK else_suitel
ifelsestmtl ::= testexpr c_stmts_opt COME_FROM JUMP_BACK else_suitel
ifelsestmtl ::= testexpr c_stmts_opt cf_jump_back else_suitel
cf_jump_back ::= COME_FROM JUMP_BACK
# FIXME: this feels like a hack. Is it just 1 or two
# COME_FROMs? the parsed tree for this and even with just the
@@ -180,14 +180,17 @@ class Python3Parser(PythonParser):
POP_BLOCK LOAD_CONST
come_from_or_finally suite_stmts_opt END_FINALLY
tryelsestmt ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK
try_middle else_suite come_from_except_clauses
tryelsestmt ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK
try_middle else_suite come_froms
tryelsestmtc ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK
try_middle else_suitec COME_FROM
try_middle else_suitec come_from_except_clauses
tryelsestmtl ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK
try_middle else_suitel COME_FROM
try_middle else_suitel come_from_except_clauses
try_middle ::= jmp_abs COME_FROM except_stmts
END_FINALLY
@@ -254,7 +257,10 @@ class Python3Parser(PythonParser):
def p_misc3(self, args):
"""
try_middle ::= JUMP_FORWARD COME_FROM_EXCEPT except_stmts END_FINALLY COME_FROM
try_middle ::= JUMP_FORWARD COME_FROM_EXCEPT except_stmts
END_FINALLY COME_FROM
try_middle ::= JUMP_FORWARD COME_FROM_EXCEPT except_stmts
END_FINALLY COME_FROM_EXCEPT_CLAUSE
for_block ::= l_stmts_opt opt_come_from_loop JUMP_BACK
for_block ::= l_stmts
@@ -269,12 +275,14 @@ class Python3Parser(PythonParser):
stmt ::= funcdef_annotate
funcdef_annotate ::= mkfunc_annotate designator
mkfuncdeco0 ::= mkfunc_annotate
# This has the annotation value.
# LOAD_NAME is used in an annotation type like
# int, float, str
annotate_arg ::= LOAD_NAME
# LOAD_CONST is used in an annotation string
annotate_arg ::= LOAD_CONST
annotate_arg ::= expr
# This stores the tuple of parameter names
# that have been annotated
@@ -285,10 +293,13 @@ class Python3Parser(PythonParser):
"""
opt_come_from_except ::= COME_FROM_EXCEPT
opt_come_from_except ::= come_froms
opt_come_from_except ::= come_from_except_clauses
come_froms ::= come_froms COME_FROM
come_froms ::=
come_froms ::= COME_FROM*
come_from_except_clauses ::= COME_FROM_EXCEPT_CLAUSE+
opt_come_from_loop ::= opt_come_from_loop COME_FROM_LOOP
opt_come_from_loop ::= opt_come_from_loop COME_FROM_LOOP
opt_come_from_loop ::=
@@ -372,6 +383,8 @@ class Python3Parser(PythonParser):
while1stmt ::= SETUP_LOOP l_stmts COME_FROM JUMP_BACK COME_FROM_LOOP
while1elsestmt ::= SETUP_LOOP l_stmts JUMP_BACK
else_suite COME_FROM_LOOP
# FIXME: investigate - can code really produce a NOP?
whileTruestmt ::= SETUP_LOOP l_stmts_opt JUMP_BACK NOP
@@ -393,9 +406,12 @@ class Python3Parser(PythonParser):
def p_expr3(self, args):
"""
conditional ::= expr jmp_false expr jf_else expr COME_FROM
conditionalnot ::= expr jmp_true expr jf_else expr COME_FROM
conditional ::= expr jmp_false expr jump_forward_else expr COME_FROM
conditionalnot ::= expr jmp_true expr jump_forward_else expr COME_FROM
# a JUMP_FORWARD to another JUMP_FORWARD can get turned into
# a JUMP_ABSOLUTE with no COME_FROM
conditional ::= expr jmp_false expr jump_absolute_else expr
expr ::= LOAD_CLASSNAME
@@ -453,9 +469,9 @@ class Python3Parser(PythonParser):
def custom_classfunc_rule(self, opname, token, customize):
"""
call_function ::= expr {expr}^n CALL_FUNCTION_n
call_function ::= expr {expr}^n CALL_FUNCTION_VAR_n POP_TOP
call_function ::= expr {expr}^n CALL_FUNCTION_VAR_KW_n POP_TOP
call_function ::= expr {expr}^n CALL_FUNCTION_KW_n POP_TOP
call_function ::= expr {expr}^n CALL_FUNCTION_VAR_n
call_function ::= expr {expr}^n CALL_FUNCTION_VAR_KW_n
call_function ::= expr {expr}^n CALL_FUNCTION_KW_n
classdefdeco2 ::= LOAD_BUILD_CLASS mkfunc {expr}^n-1 CALL_FUNCTION_n
"""
@@ -463,31 +479,56 @@ class Python3Parser(PythonParser):
# high byte number of positional parameters
args_pos = token.attr & 0xff
args_kw = (token.attr >> 8) & 0xff
# Additional exprs for * and ** args:
# 0 if neither
# 1 for CALL_FUNCTION_VAR or CALL_FUNCTION_KW
# 2 for * and ** args (CALL_FUNCTION_VAR_KW).
# Yes, this computation based on instruction name is a little bit hoaky.
nak = ( len(opname)-len('CALL_FUNCTION') ) // 3
token.type = self.call_fn_name(token)
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 where * args, the
# first LOAD_FAST, below are located.
# Python 3.6+ replaces CALL_FUNCTION_VAR_KW with CALL_FUNCTION_EX
if opname.endswith('KW'):
kw = 'LOAD_FAST '
else:
kw = ''
rule = ('call_function ::= expr expr ' +
('pos_arg ' * args_pos) +
('kwarg ' * args_kw) + kw + token.type)
self.add_unique_rule(rule, token.type, uniq_param, customize)
rule = ('call_function ::= expr ' +
('pos_arg ' * args_pos) +
('kwarg ' * args_kw) +
'expr ' * nak + token.type)
self.add_unique_rule(rule, token.type, args_pos, 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.type +
' GET_AWAITABLE LOAD_CONST YIELD_FROM')
self.add_unique_rule(rule, token.type, args_pos, customize)
self.add_unique_rule('expr ::= async_call_function', token.type, args_pos, 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.type, args_pos, 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):
@@ -625,10 +666,20 @@ class Python3Parser(PythonParser):
rule = "mapexpr ::= BUILD_MAP_n kvlist_n"
elif self.version >= 3.5:
if opname != 'BUILD_MAP_WITH_CALL':
rule = kvlist_n + ' ::= ' + 'expr ' * (token.attr*2)
self.add_unique_rule(rule, opname, token.attr, customize)
rule = "mapexpr ::= %s %s" % (kvlist_n, opname)
if opname == 'BUILD_MAP_UNPACK':
rule = kvlist_n + ' ::= ' + 'expr ' * (token.attr*2)
self.add_unique_rule(rule, opname, token.attr, customize)
rule = 'dict ::= ' + 'expr ' * (token.attr*2)
self.add_unique_rule(rule, opname, token.attr, customize)
rule = 'mapexpr ::= ' + 'dict ' * token.attr
self.add_unique_rule(rule, opname, token.attr, customize)
rule = ('unmap_dict ::= ' +
('mapexpr ' * token.attr) +
' BUILD_MAP_UNPACK')
else:
rule = kvlist_n + ' ::= ' + 'expr ' * (token.attr*2)
self.add_unique_rule(rule, opname, token.attr, customize)
rule = "mapexpr ::= %s %s" % (kvlist_n, opname)
else:
rule = kvlist_n + ' ::= ' + 'expr expr STORE_MAP ' * token.attr
self.add_unique_rule(rule, opname, token.attr, customize)
@@ -655,7 +706,10 @@ class Python3Parser(PythonParser):
rule_pat = ("genexpr ::= %sload_genexpr %%s%s expr "
"GET_ITER CALL_FUNCTION_1" % ('pos_arg '* args_pos, opname))
self.add_make_function_rule(rule_pat, opname, token.attr, customize)
rule_pat = ('mklambda ::= %sLOAD_LAMBDA %%s%s' % ('pos_arg '* args_pos, opname))
rule_pat = ('mklambda ::= %s%sLOAD_LAMBDA %%s%s' %
(('pos_arg '* args_pos),
('kwarg '* args_kw),
opname))
self.add_make_function_rule(rule_pat, opname, token.attr, customize)
rule_pat = ("listcomp ::= %sLOAD_LISTCOMP %%s%s expr "
"GET_ITER CALL_FUNCTION_1" % ('expr ' * args_pos, opname))
@@ -676,9 +730,6 @@ class Python3Parser(PythonParser):
('pos_arg ' * args_pos, opname))
self.add_unique_rule(rule, opname, token.attr, customize)
if opname.startswith('MAKE_FUNCTION_A'):
# rule = ('mkfunc2 ::= %s%sEXTENDED_ARG %s' %
# ('pos_arg ' * (args_pos), 'kwargs ' * (annotate_args-1), opname))
self.add_unique_rule(rule, opname, token.attr, customize)
if self.version >= 3.3:
rule = ('mkfunc_annotate ::= %s%sannotate_tuple LOAD_CONST LOAD_CONST EXTENDED_ARG %s' %
(('pos_arg ' * (args_pos)),
@@ -769,7 +820,8 @@ class Python3Parser(PythonParser):
elif lhs == 'annotate_tuple':
return not isinstance(tokens[first].attr, tuple)
elif lhs == 'kwarg':
return not isinstance(tokens[first].attr, str)
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?

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
@@ -20,10 +18,17 @@ class Python32Parser(Python3Parser):
whileTruestmt ::= SETUP_LOOP return_stmts
COME_FROM_LOOP
try_middle ::= JUMP_FORWARD COME_FROM_EXCEPT except_stmts
END_FINALLY
# Python 3.2+ has more loop optimization that removes
# JUMP_FORWARD in some cases, and hence we also don't
# see COME_FROM
_ifstmts_jump ::= c_stmts_opt
_ifstmts_jump ::= c_stmts_opt JUMP_FORWARD _come_from
stmt ::= del_deref_stmt
del_deref_stmt ::= DELETE_DEREF
"""
pass

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
@@ -20,7 +19,19 @@ class Python33Parser(Python32Parser):
iflaststmt ::= testexpr c_stmts_opt33
c_stmts_opt33 ::= JUMP_BACK JUMP_ABSOLUTE c_stmts_opt
_ifstmts_jump ::= c_stmts_opt JUMP_FORWARD _come_from
# Python 3.5+ has jump optimization to remove the redundant
# jump_excepts. But in 3.3 we need them added
tryelsestmt ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK
try_middle else_suite
jump_excepts come_from_except_clauses
trystmt ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK
try_middle
jump_excepts come_from_except_clauses
jump_excepts ::= jump_except+
"""
class Python33ParserSingle(Python33Parser, PythonParserSingle):

View File

@@ -15,6 +15,8 @@ class Python34Parser(Python33Parser):
def p_misc34(self, args):
"""
expr ::= LOAD_ASSERT
# Python 3.4+ optimizes the trailing two JUMPS away
# Is this 3.4 only?

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
@@ -20,12 +19,27 @@ class Python35Parser(Python34Parser):
# I'm sure by the time Python 4 comes around these will be turned
# into special opcodes
while1stmt ::= SETUP_LOOP l_stmts COME_FROM JUMP_BACK
POP_BLOCK COME_FROM_LOOP
while1stmt ::= SETUP_LOOP l_stmts COME_FROM JUMP_BACK
POP_BLOCK COME_FROM_LOOP
while1stmt ::= SETUP_LOOP l_stmts POP_BLOCK COME_FROM_LOOP
while1elsestmt ::= SETUP_LOOP l_stmts JUMP_BACK
POP_BLOCK else_suite COME_FROM_LOOP
# Python 3.5+ Await statement
stmt ::= await_stmt
await_stmt ::= call_function GET_AWAITABLE LOAD_CONST YIELD_FROM POP_TOP
expr ::= await_expr
await_expr ::= expr GET_AWAITABLE LOAD_CONST YIELD_FROM
stmt ::= await_stmt
await_stmt ::= await_expr POP_TOP
expr ::= unmap_dict
expr ::= unmapexpr
unmap_dict ::= dictcomp BUILD_MAP_UNPACK
unmap_dict ::= kv_lists BUILD_MAP_UNPACK
kv_lists ::= kv_list kv_lists
kv_lists ::= kv_list
# Python 3.5+ has WITH_CLEANUP_START/FINISH
@@ -65,20 +79,31 @@ class Python35Parser(Python34Parser):
GET_AWAITABLE LOAD_CONST YIELD_FROM
WITH_CLEANUP_FINISH END_FINALLY
stmt ::= async_for_stmt
async_for_stmt ::= SETUP_LOOP expr
GET_AITER
LOAD_CONST YIELD_FROM SETUP_EXCEPT GET_ANEXT LOAD_CONST
YIELD_FROM
designator
POP_BLOCK JUMP_FORWARD COME_FROM_EXCEPT DUP_TOP
POP_BLOCK jump_except COME_FROM_EXCEPT DUP_TOP
LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_FALSE
POP_TOP POP_TOP POP_TOP POP_EXCEPT POP_BLOCK
JUMP_ABSOLUTE END_FINALLY COME_FROM
for_block POP_BLOCK JUMP_ABSOLUTE
opt_come_from_loop
async_for_stmt ::= SETUP_LOOP expr
GET_AITER
LOAD_CONST YIELD_FROM SETUP_EXCEPT GET_ANEXT LOAD_CONST
YIELD_FROM
designator
POP_BLOCK jump_except COME_FROM_EXCEPT DUP_TOP
LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_FALSE
POP_TOP POP_TOP POP_TOP POP_EXCEPT POP_BLOCK
JUMP_ABSOLUTE END_FINALLY JUMP_BACK
passstmt POP_BLOCK JUMP_ABSOLUTE
opt_come_from_loop
stmt ::= async_forelse_stmt
async_forelse_stmt ::= SETUP_LOOP expr
GET_AITER
@@ -112,7 +137,6 @@ class Python35Parser(Python34Parser):
# differently than 3.3, 3.4
yield_from ::= expr GET_YIELD_FROM_ITER LOAD_CONST YIELD_FROM
"""
def add_custom_rules(self, tokens, customize):

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
@@ -16,6 +15,8 @@ class Python36Parser(Python35Parser):
def p_36misc(self, args):
"""
expr ::= LOAD_NAME EXTENDED_ARG
fstring_multi ::= fstring_expr_or_strs BUILD_STRING
fstring_expr_or_strs ::= fstring_expr_or_str+
@@ -24,12 +25,16 @@ class Python36Parser(Python35Parser):
withstmt ::= expr SETUP_WITH POP_TOP suite_stmts_opt POP_BLOCK LOAD_CONST
WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
call_function ::= expr expr CALL_FUNCTION_EX
call_function ::= expr expr expr CALL_FUNCTION_EX_KW
"""
def add_custom_rules(self, tokens, customize):
super(Python36Parser, self).add_custom_rules(tokens, customize)
for i, token in enumerate(tokens):
opname = token.type
if opname == 'FORMAT_VALUE':
rules_str = """
expr ::= fstring_single

View File

@@ -10,8 +10,6 @@ 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
@@ -66,6 +64,12 @@ class Scanner(object):
# FIXME: This weird Python2 behavior is not Python3
self.resetTokenClass()
def opname_for_offset(self, offset):
return self.opc.opname[self.code[offset]]
def op_name(self, op):
return self.opc.opname[op]
def is_jump_forward(self, offset):
"""
Return True if the code at offset is some sort of jump forward.
@@ -226,9 +230,15 @@ class Scanner(object):
for given opcode <op>.
"""
if op < self.opc.HAVE_ARGUMENT:
return 2 if self.version >= 3.6 else 1
if self.version >= 3.6:
return 2
else:
return 1
else:
return 2 if self.version >= 3.6 else 3
if self.version >= 3.6:
return 2
else:
return 3
def remove_mid_line_ifs(self, ifs):
"""

View File

@@ -13,13 +13,13 @@ import uncompyle6.scanners.scanner21 as scan
from xdis.opcodes import opcode_15
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

@@ -1,4 +1,4 @@
# Copyright (c) 2015, 2016 by Rocky Bernstein
# Copyright (c) 2015-2017 by Rocky Bernstein
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
"""
@@ -20,9 +20,13 @@ For example:
Finally we save token information.
"""
from __future__ import print_function
from uncompyle6 import PYTHON_VERSION
if PYTHON_VERSION < 2.6:
from xdis.namedtuple25 import namedtuple
else:
from collections import namedtuple
from collections import namedtuple
from array import array
from uncompyle6.scanner import op_has_argument
@@ -84,7 +88,9 @@ class Scanner2(scan.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
@@ -164,7 +170,7 @@ class Scanner2(scan.Scanner):
# continue
# last_offset = jump_offset
come_from_name = 'COME_FROM'
op_name = self.opc.opname[self.code[jump_offset]]
op_name = self.opname_for_offset(jump_offset)
if op_name.startswith('SETUP_') and self.version == 2.7:
come_from_type = op_name[len('SETUP_'):]
if come_from_type not in ('LOOP', 'EXCEPT'):
@@ -178,7 +184,7 @@ class Scanner2(scan.Scanner):
pass
op = self.code[offset]
op_name = self.opc.opname[op]
op_name = self.op_name(op)
oparg = None; pattr = None
has_arg = op_has_argument(op, self.opc)
@@ -406,7 +412,7 @@ class Scanner2(scan.Scanner):
while code[j] == self.opc.JUMP_ABSOLUTE:
j = self.prev[j]
if (self.version >= 2.3 and
self.opc.opname[code[j]] == 'LIST_APPEND'): # list comprehension
self.opname_for_offset(j) == 'LIST_APPEND'): # list comprehension
stmts.remove(s)
continue
elif code[s] == self.opc.POP_TOP:
@@ -844,7 +850,7 @@ class Scanner2(scan.Scanner):
# is a jump to a SETUP_LOOP target.
next_offset = target + self.op_size(self.code[target])
next_op = self.code[next_offset]
if self.opc.opname[next_op] == 'JUMP_FORWARD':
if self.op_name(next_op) == 'JUMP_FORWARD':
jump_target = self.get_target(next_offset, next_op)
if jump_target in self.setup_loops:
self.structs.append({'type': 'while-loop',
@@ -882,12 +888,12 @@ class Scanner2(scan.Scanner):
# 39_0 COME_FROM 3
# 40 ...
if self.opc.opname[code[jump_if_offset]].startswith('JUMP_IF'):
if self.opname_for_offset(jump_if_offset).startswith('JUMP_IF'):
jump_if_target = code[jump_if_offset+1]
if self.opc.opname[code[jump_if_target + jump_if_offset + 3]] == 'POP_TOP':
if self.opname_for_offset(jump_if_target + jump_if_offset + 3) == 'POP_TOP':
jump_inst = jump_if_target + jump_if_offset
jump_offset = code[jump_inst+1]
jump_op = self.opc.opname[code[jump_inst]]
jump_op = self.opname_for_offset(jump_inst)
if (jump_op == 'JUMP_FORWARD' and jump_offset == 1):
self.structs.append({'type': 'if-then',
'start': start-3,
@@ -922,7 +928,7 @@ class Scanner2(scan.Scanner):
# 256
if if_then_maybe and jump_op == 'JUMP_ABSOLUTE':
jump_target = self.get_target(jump_inst, code[jump_inst])
if self.opc.opname[code[end]] == 'JUMP_FORWARD':
if self.opname_for_offset(end) == 'JUMP_FORWARD':
end_target = self.get_target(end, code[end])
if jump_target == end_target:
self.structs.append(if_then_maybe)
@@ -991,7 +997,7 @@ class Scanner2(scan.Scanner):
oparg = self.get_argument(offset)
if label is None:
if op in self.opc.hasjrel and self.opc.opname[op] != 'FOR_ITER':
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
@@ -1023,8 +1029,10 @@ class Scanner2(scan.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

@@ -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

@@ -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

View File

@@ -86,7 +86,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

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2015, 2016 by Rocky Bernstein
# Copyright (c) 2015-2017 by Rocky Bernstein
"""
Python 2.7 bytecode ingester.
@@ -7,8 +7,6 @@ grammar parsing.
"""
from __future__ import print_function
from uncompyle6.scanners.scanner2 import Scanner2
from uncompyle6 import PYTHON3
@@ -48,7 +46,7 @@ class Scanner27(Scanner2):
self.opc.DELETE_SLICE_2, self.opc.DELETE_SLICE_3,
])
# opcodes with expect a variable number pushed values whose
# opcodes which expect a variable number pushed values and whose
# count is in the opcode. For parsing we generally change the
# opcode name to include that number.
varargs_ops = set([

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2015, 2016 by Rocky Bernstein
# Copyright (c) 2015-2017 by Rocky Bernstein
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
"""
@@ -20,9 +20,13 @@ For example:
Finally we save token information.
"""
from __future__ import print_function
from uncompyle6 import PYTHON_VERSION
if PYTHON_VERSION < 2.6:
from xdis.namedtuple25 import namedtuple
else:
from collections import namedtuple
from collections import namedtuple
from array import array
from uncompyle6.scanner import Scanner, op_has_argument
@@ -40,9 +44,6 @@ if PYTHON3:
globals().update(op3.opmap)
# POP_JUMP_IF is used by verify
POP_JUMP_TF = (POP_JUMP_IF_TRUE, POP_JUMP_IF_FALSE)
class Scanner3(Scanner):
def __init__(self, version, show_asm=None, is_pypy=False):
@@ -61,6 +62,11 @@ class Scanner3(Scanner):
setup_ops.append(self.opc.SETUP_WITH)
self.setup_ops = frozenset(setup_ops)
if self.version == 3.0:
self.pop_jump_tf = frozenset([self.opc.JUMP_IF_FALSE, self.opc.JUMP_IF_TRUE])
else:
self.pop_jump_tf = frozenset([self.opc.PJIF, self.opc.PJIT])
self.setup_ops_no_loop = frozenset(setup_ops) - frozenset([self.opc.SETUP_LOOP])
# Opcodes that can start a statement.
@@ -133,9 +139,6 @@ class Scanner3(Scanner):
# FIXME: remove the above in favor of:
# self.varargs_ops = frozenset(self.opc.hasvargs)
def opName(self, offset):
return self.opc.opname[self.code[offset]]
def ingest(self, co, classname=None, code_objects={}, show_asm=None):
"""
Pick out tokens from an uncompyle6 code object, and transform them,
@@ -152,8 +155,10 @@ 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):
@@ -219,11 +224,16 @@ class Scanner3(Scanner):
# "loop" tag last so the grammar rule matches that properly.
for jump_offset in sorted(jump_targets[inst.offset], reverse=True):
come_from_name = 'COME_FROM'
opname = self.opName(jump_offset)
opname = self.opname_for_offset(jump_offset)
if opname.startswith('SETUP_'):
come_from_type = opname[len('SETUP_'):]
come_from_name = 'COME_FROM_%s' % come_from_type
pass
elif inst.offset in self.except_targets:
come_from_name = 'COME_FROM_EXCEPT_CLAUSE'
if self.version <= 3.2:
continue
pass
tokens.append(Token(come_from_name,
None, repr(jump_offset),
offset='%s_%s' % (inst.offset, jump_idx),
@@ -446,6 +456,7 @@ class Scanner3(Scanner):
# Map fixed jumps to their real destination
self.fixed_jumps = {}
self.except_targets = {}
self.ignore_if = set()
self.build_statement_indices()
self.else_start = {}
@@ -660,7 +671,7 @@ class Scanner3(Scanner):
jump_back += 2
if_offset = None
if code[self.prev_op[next_line_byte]] not in POP_JUMP_TF:
if code[self.prev_op[next_line_byte]] not in self.pop_jump_tf:
if_offset = self.prev[next_line_byte]
if if_offset:
loop_type = 'while'
@@ -705,7 +716,7 @@ class Scanner3(Scanner):
self.structs.append({'type': loop_type + '-else',
'start': jump_back+3,
'end': end})
elif op in POP_JUMP_TF:
elif op in self.pop_jump_tf:
start = offset + self.op_size(op)
target = self.get_target(offset)
rtarget = self.restrict_to_parent(target, parent)
@@ -752,12 +763,12 @@ class Scanner3(Scanner):
target == self.get_target(prev_op[pre_rtarget]) and
(prev_op[pre_rtarget] not in self.stmts or
self.get_target(prev_op[pre_rtarget]) > prev_op[pre_rtarget]) and
1 == len(self.remove_mid_line_ifs(self.rem_or(start, prev_op[pre_rtarget], POP_JUMP_TF, target)))):
1 == len(self.remove_mid_line_ifs(self.rem_or(start, prev_op[pre_rtarget], self.pop_jump_tf, target)))):
pass
elif (code[prev_op[pre_rtarget]] == self.opc.RETURN_VALUE
and self.remove_mid_line_ifs([offset]) and
1 == (len(set(self.remove_mid_line_ifs(self.rem_or(start, prev_op[pre_rtarget],
POP_JUMP_TF, target))) |
self.pop_jump_tf, target))) |
set(self.remove_mid_line_ifs(self.rem_or(start, prev_op[pre_rtarget],
(self.opc.POP_JUMP_IF_FALSE,
self.opc.POP_JUMP_IF_TRUE,
@@ -840,6 +851,9 @@ class Scanner3(Scanner):
self.structs.append({'type': 'if-then',
'start': start,
'end': pre_rtarget})
# FIXME: add this
# self.fixed_jumps[offset] = rtarget
self.not_continue.add(pre_rtarget)
if rtarget < end and (
@@ -888,7 +902,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
@@ -902,6 +916,16 @@ class Scanner3(Scanner):
target = self.get_target(offset)
end = self.restrict_to_parent(target, parent)
self.fixed_jumps[offset] = end
elif op == self.opc.POP_EXCEPT:
if self.version <= 3.5:
next_offset = offset+1
else:
next_offset = offset+2
target = self.get_target(next_offset)
if target > next_offset:
self.fixed_jumps[next_offset] = target
self.except_targets[target] = next_offset
elif op == self.opc.SETUP_FINALLY:
target = self.get_target(offset)
end = self.restrict_to_parent(target, parent)

View File

@@ -6,8 +6,6 @@ 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
JUMP_OPs = map(lambda op: opc.opname[op], opc.hasjrel + opc.hasjabs)

View File

@@ -6,8 +6,6 @@ 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 = map(lambda op: opc.opname[op], opc.hasjrel + opc.hasjabs)

Some files were not shown because too many files have changed in this diff Show More