You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-03 00:45:53 +08:00
Start process of making python3 compatible
This commit is contained in:
15
.travis.yml
Normal file
15
.travis.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
language: python
|
||||||
|
|
||||||
|
sudo: false
|
||||||
|
|
||||||
|
python:
|
||||||
|
- '2.7'
|
||||||
|
|
||||||
|
install:
|
||||||
|
- pip install -r requirements.txt
|
||||||
|
- pip install -r requirements-dev.txt
|
||||||
|
- pip install .
|
||||||
|
- if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install unittest2; fi
|
||||||
|
|
||||||
|
script:
|
||||||
|
- make test
|
95
Makefile
Normal file
95
Makefile
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
# Compatibility for us old-timers.
|
||||||
|
|
||||||
|
# Note: This makefile include remake-style target comments.
|
||||||
|
# These comments before the targets start with #:
|
||||||
|
# remake --tasks to shows the targets and the comments
|
||||||
|
|
||||||
|
GIT2CL ?= git2cl
|
||||||
|
PYTHON ?= python
|
||||||
|
PYTHON3 ?= python3
|
||||||
|
RM ?= rm
|
||||||
|
LINT = flake8
|
||||||
|
|
||||||
|
#EXTRA_DIST=ipython/ipy_trepan.py trepan
|
||||||
|
PHONY=check clean dist distclean test test-unit test-functional rmChangeLog clean_pyc nosetests
|
||||||
|
|
||||||
|
#: Default target - same as "check"
|
||||||
|
all: check
|
||||||
|
|
||||||
|
#: Make HTML docs
|
||||||
|
html:
|
||||||
|
cd docs && $(MAKE) html
|
||||||
|
|
||||||
|
#: Same as "check"
|
||||||
|
test: check
|
||||||
|
|
||||||
|
#: Same as "check"
|
||||||
|
nosetests: check
|
||||||
|
|
||||||
|
#: Run all tests
|
||||||
|
check-short: test-unit-short
|
||||||
|
|
||||||
|
#: Run all tests: unit, functional and integration verbosely
|
||||||
|
check: test-unit lint
|
||||||
|
|
||||||
|
#: Run unit (white-box) tests
|
||||||
|
test-unit:
|
||||||
|
$(PYTHON) ./setup.py nosetests
|
||||||
|
|
||||||
|
#: Run unit (white-box) tests
|
||||||
|
test-unit-short:
|
||||||
|
$(PYTHON) ./setup.py nosetests --quiet 2>&1 | \
|
||||||
|
$(PYTHON) ./test/make-check-filter.py
|
||||||
|
|
||||||
|
#: Clean up temporary files and .pyc files
|
||||||
|
clean: clean_pyc
|
||||||
|
$(PYTHON) ./setup.py $@
|
||||||
|
|
||||||
|
#: Create source (tarball) and binary (egg) distribution
|
||||||
|
dist:
|
||||||
|
$(PYTHON) ./setup.py sdist bdist_egg
|
||||||
|
|
||||||
|
#: Remove .pyc files
|
||||||
|
clean_pyc:
|
||||||
|
$(RM) -f */*.pyc */*/*.pyc */*/*/*.pyc */*/*/*/*.pyc
|
||||||
|
|
||||||
|
#: Create source tarball
|
||||||
|
sdist:
|
||||||
|
$(PYTHON) ./setup.py sdist
|
||||||
|
|
||||||
|
|
||||||
|
#: Style check. Set env var LINT to pyflakes, flake, or flake8
|
||||||
|
lint:
|
||||||
|
$(LINT) trepan_deparse/deparser.py
|
||||||
|
|
||||||
|
#: Create binary egg distribution
|
||||||
|
bdist_egg:
|
||||||
|
$(PYTHON) ./setup.py bdist_egg
|
||||||
|
|
||||||
|
|
||||||
|
# It is too much work to figure out how to add a new command to distutils
|
||||||
|
# to do the following. I'm sure distutils will someday get there.
|
||||||
|
DISTCLEAN_FILES = build dist *.pyc
|
||||||
|
|
||||||
|
#: Remove ALL derived files
|
||||||
|
distclean: clean
|
||||||
|
-rm -fr $(DISTCLEAN_FILES) || true
|
||||||
|
-find . -name \*.pyc -exec rm -v {} \;
|
||||||
|
-find . -name \*.egg-info -exec rm -vr {} \;
|
||||||
|
|
||||||
|
#: Install package locally
|
||||||
|
verbose-install:
|
||||||
|
$(PYTHON) ./setup.py install
|
||||||
|
|
||||||
|
#: Install package locally without the verbiage
|
||||||
|
install:
|
||||||
|
$(PYTHON) ./setup.py install >/dev/null
|
||||||
|
|
||||||
|
rmChangeLog:
|
||||||
|
rm ChangeLog || true
|
||||||
|
|
||||||
|
#: Create a ChangeLog from git via git log and git2cl
|
||||||
|
ChangeLog: rmChangeLog
|
||||||
|
git log --pretty --numstat --summary | $(GIT2CL) >$@
|
||||||
|
|
||||||
|
.PHONY: $(PHONY)
|
@@ -4,12 +4,12 @@
|
|||||||
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
||||||
#
|
#
|
||||||
'''
|
'''
|
||||||
Usage: uncompyle2 [OPTIONS]... [ FILE | DIR]...
|
Usage: uncompyle6 [OPTIONS]... [ FILE | DIR]...
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
uncompyle2 foo.pyc bar.pyc # decompile foo.pyc, bar.pyc to stdout
|
uncompyle6 foo.pyc bar.pyc # decompile foo.pyc, bar.pyc to stdout
|
||||||
uncompyle2 -o . foo.pyc bar.pyc # decompile to ./foo.pyc_dis and ./bar.pyc_dis
|
uncompyle6 -o . foo.pyc bar.pyc # decompile to ./foo.pyc_dis and ./bar.pyc_dis
|
||||||
uncompyle2 -o /tmp /usr/lib/python1.5 # decompile whole library
|
uncompyle6 -o /tmp /usr/lib/python1.5 # decompile whole library
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-o <path> output decompiled files to this path:
|
-o <path> output decompiled files to this path:
|
||||||
@@ -41,15 +41,15 @@ Extensions of generated files:
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
Usage_short = \
|
Usage_short = \
|
||||||
"uncompyle2 [--help] [--verify] [--showasm] [--showast] [-o <path>] FILE|DIR..."
|
"uncompyle6 [--help] [--verify] [--showasm] [--showast] [-o <path>] FILE|DIR..."
|
||||||
|
|
||||||
import sys, os, getopt
|
import sys, os, getopt
|
||||||
import os.path
|
import os.path
|
||||||
from uncompyle2 import main, verify
|
from uncompyle6 import main, verify
|
||||||
import time
|
import time
|
||||||
|
|
||||||
if sys.version[:3] != '2.7':
|
if sys.version[:3] != '2.7':
|
||||||
print >>sys.stderr, 'Error: uncompyle2 requires Python 2.7.'
|
print >>sys.stderr, 'Error: uncompyle6 requires Python 2.7.'
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
showasm = showast = do_verify = numproc = recurse_dirs = 0
|
showasm = showast = do_verify = numproc = recurse_dirs = 0
|
8
setup.py
8
setup.py
@@ -4,12 +4,12 @@
|
|||||||
|
|
||||||
from distutils.core import setup, Extension
|
from distutils.core import setup, Extension
|
||||||
|
|
||||||
setup (name = "uncompyle2",
|
setup (name = "uncompyle6",
|
||||||
version = "1.1",
|
version = "2.0",
|
||||||
description = "Python byte-code to source-code converter",
|
description = "Python byte-code to source-code converter",
|
||||||
author = "Mysterie",
|
author = "Mysterie",
|
||||||
author_email = "kajusska@gmail.com",
|
author_email = "kajusska@gmail.com",
|
||||||
url = "http://github.com/Mysterie/uncompyle2",
|
url = "http://github.com/Mysterie/uncompyle2",
|
||||||
packages=['uncompyle2', 'uncompyle2.opcode'],
|
packages=['uncompyle6', 'uncompyle6.opcode'],
|
||||||
scripts=['scripts/uncompyle2']
|
scripts=['scripts/uncompyle6']
|
||||||
)
|
)
|
||||||
|
18
test_one
Executable file
18
test_one
Executable file
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
PAGER=${PAGER:-less}
|
||||||
|
file=$1
|
||||||
|
shift
|
||||||
|
options=$@
|
||||||
|
|
||||||
|
#BASEDIR=test/bytecode_1.5
|
||||||
|
#BASEDIR=test/bytecode_2.0
|
||||||
|
#BASEDIR=test/bytecode_2.1
|
||||||
|
#BASEDIR=test/bytecode_2.2
|
||||||
|
BASEDIR=test/bytecode_2.7
|
||||||
|
|
||||||
|
if [[ `dirname $file` == '.' ]] ; then
|
||||||
|
file=$BASEDIR/test_$file.pyc
|
||||||
|
fi
|
||||||
|
|
||||||
|
python -u ./scripts/uncompyle6 $options $file 2>&1 | $PAGER
|
19
tox.ini
Normal file
19
tox.ini
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
; Settings file for flake8:
|
||||||
|
; http://flake8.readthedocs.org/en/latest/config.html#settings
|
||||||
|
[flake8]
|
||||||
|
exclude = .tox,./build,./trepan/processor/command/tmp
|
||||||
|
filename = *.py
|
||||||
|
ignore = C901,E113,E121,E122,E123,E125,E126,E127,E128,E129,E201,E202,E203,E221,E222,E225,E226,E241,E242,E251,E261,E271,E272,E302,E401,E501,F401,E701,E702
|
||||||
|
|
||||||
|
[tox]
|
||||||
|
envlist = py26, py27, pypy
|
||||||
|
|
||||||
|
[testenv]
|
||||||
|
deps =
|
||||||
|
requests>=0.8.8
|
||||||
|
mock>=1.0.1
|
||||||
|
commands = python -W always setup.py nosetests {posargs}
|
||||||
|
|
||||||
|
[testenv:py27]
|
||||||
|
deps =
|
||||||
|
flake8
|
@@ -3,6 +3,9 @@ from uncompyle2 import uncompyle, walker, verify, magics
|
|||||||
from uncompyle2.spark import GenericASTTraversal, GenericASTTraversalPruningException
|
from uncompyle2.spark import GenericASTTraversal, GenericASTTraversalPruningException
|
||||||
import sys, inspect, types, cStringIO
|
import sys, inspect, types, cStringIO
|
||||||
|
|
||||||
|
from collections import namedtuple
|
||||||
|
NodeInfo = namedtuple("NodeInfo", "node start finish")
|
||||||
|
|
||||||
class FindWalker(walker.Walker, object):
|
class FindWalker(walker.Walker, object):
|
||||||
stacked_params = ('f', 'indent', 'isLambda', '_globals')
|
stacked_params = ('f', 'indent', 'isLambda', '_globals')
|
||||||
|
|
||||||
@@ -22,6 +25,10 @@ class FindWalker(walker.Walker, object):
|
|||||||
self.currentclass = None
|
self.currentclass = None
|
||||||
self.pending_newlines = 0
|
self.pending_newlines = 0
|
||||||
|
|
||||||
|
self.found_offset = False
|
||||||
|
self.offsets = {}
|
||||||
|
|
||||||
|
|
||||||
f = property(lambda s: s.__params['f'],
|
f = property(lambda s: s.__params['f'],
|
||||||
lambda s, x: s.__params.__setitem__('f', x),
|
lambda s, x: s.__params.__setitem__('f', x),
|
||||||
lambda s: s.__params.__delitem__('f'),
|
lambda s: s.__params.__delitem__('f'),
|
||||||
@@ -47,10 +54,10 @@ class FindWalker(walker.Walker, object):
|
|||||||
node = self.ast
|
node = self.ast
|
||||||
|
|
||||||
if hasattr(node, 'offset'):
|
if hasattr(node, 'offset'):
|
||||||
print "Name %s has an offset %d" % (self.typestring(node), node.offset)
|
start = len(self.f.getvalue())
|
||||||
if node.offset == self.find_offset:
|
if node.offset == self.find_offset:
|
||||||
self.found_offset = True
|
self.found_offset = True
|
||||||
print 'BINGO!'
|
# print 'BINGO!'
|
||||||
|
|
||||||
try:
|
try:
|
||||||
name = 'n_' + self.typestring(node)
|
name = 'n_' + self.typestring(node)
|
||||||
@@ -60,6 +67,12 @@ class FindWalker(walker.Walker, object):
|
|||||||
else:
|
else:
|
||||||
self.default(node)
|
self.default(node)
|
||||||
except GenericASTTraversalPruningException:
|
except GenericASTTraversalPruningException:
|
||||||
|
if hasattr(node, 'offset'):
|
||||||
|
self.offsets[node.offset] = NodeInfo(node = node,
|
||||||
|
start = start,
|
||||||
|
finish = len(self.f.getvalue()))
|
||||||
|
# print self.offsets[node.offset]
|
||||||
|
# print self.f.getvalue()[start:]
|
||||||
return
|
return
|
||||||
|
|
||||||
for kid in node:
|
for kid in node:
|
||||||
@@ -70,6 +83,9 @@ class FindWalker(walker.Walker, object):
|
|||||||
func = getattr(self, name)
|
func = getattr(self, name)
|
||||||
func(node)
|
func(node)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
def find_source(self, offset, ast, customize, isLambda=0, returnNone=False):
|
def find_source(self, offset, ast, customize, isLambda=0, returnNone=False):
|
||||||
"""convert AST to source code"""
|
"""convert AST to source code"""
|
||||||
|
|
||||||
@@ -86,10 +102,11 @@ class FindWalker(walker.Walker, object):
|
|||||||
self.print_(self.indent, 'pass')
|
self.print_(self.indent, 'pass')
|
||||||
else:
|
else:
|
||||||
self.customize(customize)
|
self.customize(customize)
|
||||||
|
result = self.traverse(ast, isLambda=isLambda)
|
||||||
if isLambda:
|
if isLambda:
|
||||||
self.write(self.traverse(ast, isLambda=isLambda))
|
self.write(result)
|
||||||
else:
|
else:
|
||||||
self.print_(self.traverse(ast, isLambda=isLambda))
|
self.print_(result)
|
||||||
self.return_none = rn
|
self.return_none = rn
|
||||||
|
|
||||||
# FIXME; below duplicated the code, since we don't find self.__params
|
# FIXME; below duplicated the code, since we don't find self.__params
|
||||||
@@ -172,7 +189,7 @@ def uncompyle_test():
|
|||||||
uncompyle(2.7, co, sys.stdout, 1)
|
uncompyle(2.7, co, sys.stdout, 1)
|
||||||
print
|
print
|
||||||
print '------------------------'
|
print '------------------------'
|
||||||
uncompyle_find(2.7, co, 24)
|
uncompyle_find(2.7, co, 33)
|
||||||
finally:
|
finally:
|
||||||
del frame
|
del frame
|
||||||
|
|
||||||
|
@@ -1,86 +0,0 @@
|
|||||||
import struct
|
|
||||||
|
|
||||||
__all__ = ['magics', 'versions']
|
|
||||||
|
|
||||||
def __build_magic(magic):
|
|
||||||
return struct.pack('Hcc', magic, '\r', '\n')
|
|
||||||
|
|
||||||
def __by_version(magics):
|
|
||||||
by_version = {}
|
|
||||||
for m, v in magics.items():
|
|
||||||
by_version[v] = m
|
|
||||||
return by_version
|
|
||||||
|
|
||||||
versions = {
|
|
||||||
# taken from from Python/import.c
|
|
||||||
# magic, version
|
|
||||||
__build_magic(20121): '1.5', #1.5, 1.5.1, 1.5.2
|
|
||||||
__build_magic(50428): '1.6', #1.6
|
|
||||||
__build_magic(50823): '2.0', #2.0, 2.0.1
|
|
||||||
__build_magic(60202): '2.1', #2.1, 2.1.1, 2.1.2
|
|
||||||
__build_magic(60717): '2.2', #2.2
|
|
||||||
__build_magic(62011): '2.3', #2.3a0
|
|
||||||
__build_magic(62021): '2.3', #2.3a0
|
|
||||||
__build_magic(62041): '2.4', #2.4a0
|
|
||||||
__build_magic(62051): '2.4', #2.4a3
|
|
||||||
__build_magic(62061): '2.4', #2.4b1
|
|
||||||
__build_magic(62071): '2.5', #2.5a0
|
|
||||||
__build_magic(62081): '2.5', #2.5a0 (ast-branch)
|
|
||||||
__build_magic(62091): '2.5', #2.5a0 (with)
|
|
||||||
__build_magic(62092): '2.5', #2.5a0 (changed WITH_CLEANUP opcode)
|
|
||||||
__build_magic(62101): '2.5', #2.5b3 (fix wrong code: for x, in ...)
|
|
||||||
__build_magic(62111): '2.5', #2.5b3 (fix wrong code: x += yield)
|
|
||||||
__build_magic(62121): '2.5', #2.5c1 (fix wrong lnotab with for loops and
|
|
||||||
# storing constants that should have been removed
|
|
||||||
__build_magic(62131): '2.5', #2.5c2 (fix wrong code: for x, in ... in listcomp/genexp)
|
|
||||||
__build_magic(62151): '2.6', #2.6a0 (peephole optimizations & STORE_MAP)
|
|
||||||
__build_magic(62161): '2.6', #2.6a1 (WITH_CLEANUP optimization)
|
|
||||||
__build_magic(62171): '2.7', #2.7a0 (optimize list comprehensions/change LIST_APPEND)
|
|
||||||
__build_magic(62181): '2.7', #2.7a0 (optimize conditional branches:
|
|
||||||
# introduce POP_JUMP_IF_FALSE and POP_JUMP_IF_TRUE)
|
|
||||||
__build_magic(62191): '2.7', #2.7a0 (introduce SETUP_WITH)
|
|
||||||
__build_magic(62201): '2.7', #2.7a0 (introduce BUILD_SET)
|
|
||||||
__build_magic(62211): '2.7', #2.7a0 (introduce MAP_ADD and SET_ADD)
|
|
||||||
__build_magic(3000): '3.0', #3.000
|
|
||||||
__build_magic(3010): '3.0', #3.000 (removed UNARY_CONVERT)
|
|
||||||
__build_magic(3020): '3.0', #3.000 (added BUILD_SET)
|
|
||||||
__build_magic(3030): '3.0', #3.000 (added keyword-only parameters)
|
|
||||||
__build_magic(3040): '3.0', #3.000 (added signature annotations)
|
|
||||||
__build_magic(3050): '3.0', #3.000 (print becomes a function)
|
|
||||||
__build_magic(3060): '3.0', #3.000 (PEP 3115 metaclass syntax)
|
|
||||||
__build_magic(3061): '3.0', #3.000 (string literals become unicode)
|
|
||||||
__build_magic(3071): '3.0', #3.000 (PEP 3109 raise changes)
|
|
||||||
__build_magic(3081): '3.0', #3.000 (PEP 3137 make __file__ and __name__ unicode)
|
|
||||||
__build_magic(3091): '3.0', #3.000 (kill str8 interning)
|
|
||||||
__build_magic(3101): '3.0', #3.000 (merge from 2.6a0, see 62151)
|
|
||||||
__build_magic(3103): '3.0', #3.000 (__file__ points to source file)
|
|
||||||
__build_magic(3111): '3.0', #3.0a4 (WITH_CLEANUP optimization).
|
|
||||||
__build_magic(3131): '3.0', #3.0a5 (lexical exception stacking, including POP_EXCEPT)
|
|
||||||
__build_magic(3141): '3.1', #3.1a0 (optimize list, set and dict comprehensions)
|
|
||||||
__build_magic(3151): '3.1', #3.1a0 (optimize conditional branches)
|
|
||||||
__build_magic(3160): '3.2', #3.2a0 (add SETUP_WITH)
|
|
||||||
__build_magic(3170): '3.2', #3.2a1 (add DUP_TOP_TWO, remove DUP_TOPX and ROT_FOUR)
|
|
||||||
__build_magic(3180): '3.2', #3.2a2 (add DELETE_DEREF)
|
|
||||||
}
|
|
||||||
|
|
||||||
magics = __by_version(versions)
|
|
||||||
|
|
||||||
def __show(text, magic):
|
|
||||||
print text, struct.unpack('BBBB', magic), \
|
|
||||||
struct.unpack('HBB', magic)
|
|
||||||
|
|
||||||
def test():
|
|
||||||
import imp
|
|
||||||
magic_20 = by_version['2.0']
|
|
||||||
current = imp.get_magic()
|
|
||||||
current_version = magics[current]
|
|
||||||
magic_current = by_version[ current_version ]
|
|
||||||
print type(magic_20), len(magic_20), repr(magic_20)
|
|
||||||
print
|
|
||||||
print 'This Python interpreter has version', current_version
|
|
||||||
__show('imp.get_magic():\t', current),
|
|
||||||
__show('magic[current_version]:\t', magic_current)
|
|
||||||
__show('magic_20:\t\t', magic_20)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
test()
|
|
@@ -1,277 +0,0 @@
|
|||||||
# Copyright (c) 1999 John Aycock
|
|
||||||
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
|
||||||
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
|
|
||||||
#
|
|
||||||
# See main module for license.
|
|
||||||
#
|
|
||||||
|
|
||||||
__all__ = ['Token', 'Scanner', 'Code']
|
|
||||||
|
|
||||||
import types
|
|
||||||
from collections import namedtuple
|
|
||||||
from array import array
|
|
||||||
from operator import itemgetter
|
|
||||||
|
|
||||||
from uncompyle2.opcode import opcode_25, opcode_26, opcode_27
|
|
||||||
|
|
||||||
class Token:
|
|
||||||
'''
|
|
||||||
Class representing a byte-code token.
|
|
||||||
|
|
||||||
A byte-code token is equivalent to the contents of one line
|
|
||||||
as output by dis.dis().
|
|
||||||
'''
|
|
||||||
def __init__(self, type_, attr=None, pattr=None, offset=-1, linestart=False):
|
|
||||||
self.type = intern(type_)
|
|
||||||
self.attr = attr
|
|
||||||
self.pattr = pattr
|
|
||||||
self.offset = offset
|
|
||||||
self.linestart = linestart
|
|
||||||
|
|
||||||
def __cmp__(self, o):
|
|
||||||
if isinstance(o, Token):
|
|
||||||
# both are tokens: compare type and pattr
|
|
||||||
return cmp(self.type, o.type) or cmp(self.pattr, o.pattr)
|
|
||||||
else:
|
|
||||||
return cmp(self.type, o)
|
|
||||||
|
|
||||||
def __repr__(self): return str(self.type)
|
|
||||||
def __str__(self):
|
|
||||||
pattr = self.pattr
|
|
||||||
if self.linestart:
|
|
||||||
return '\n%s\t%-17s %r' % (self.offset, self.type, pattr)
|
|
||||||
else:
|
|
||||||
return '%s\t%-17s %r' % (self.offset, self.type, pattr)
|
|
||||||
|
|
||||||
def __hash__(self): return hash(self.type)
|
|
||||||
def __getitem__(self, i): raise IndexError
|
|
||||||
|
|
||||||
class Code:
|
|
||||||
'''
|
|
||||||
Class for representing code-objects.
|
|
||||||
|
|
||||||
This is similar to the original code object, but additionally
|
|
||||||
the diassembled code is stored in the attribute '_tokens'.
|
|
||||||
'''
|
|
||||||
def __init__(self, co, scanner, classname=None):
|
|
||||||
for i in dir(co):
|
|
||||||
if i.startswith('co_'):
|
|
||||||
setattr(self, i, getattr(co, i))
|
|
||||||
self._tokens, self._customize = scanner.disassemble(co, classname)
|
|
||||||
|
|
||||||
class Scanner(object):
|
|
||||||
opc = None # opcode module
|
|
||||||
|
|
||||||
def __init__(self, version):
|
|
||||||
if version == 2.7:
|
|
||||||
self.opc = opcode_27
|
|
||||||
elif version == 2.6:
|
|
||||||
self.opc = opcode_26
|
|
||||||
elif version == 2.5:
|
|
||||||
self.opc = opcode_25
|
|
||||||
|
|
||||||
return self.resetTokenClass()
|
|
||||||
|
|
||||||
def setShowAsm(self, showasm, out=None):
|
|
||||||
self.showasm = showasm
|
|
||||||
self.out = out
|
|
||||||
|
|
||||||
def setTokenClass(self, tokenClass):
|
|
||||||
assert type(tokenClass) == types.ClassType
|
|
||||||
self.Token = tokenClass
|
|
||||||
return self.Token
|
|
||||||
|
|
||||||
def resetTokenClass(self):
|
|
||||||
return self.setTokenClass(Token)
|
|
||||||
|
|
||||||
def get_target(self, pos, op=None):
|
|
||||||
if op is None:
|
|
||||||
op = self.code[pos]
|
|
||||||
target = self.get_argument(pos)
|
|
||||||
if op in self.opc.hasjrel:
|
|
||||||
target += pos + 3
|
|
||||||
return target
|
|
||||||
|
|
||||||
def get_argument(self, pos):
|
|
||||||
arg = self.code[pos+1] + self.code[pos+2] * 256
|
|
||||||
return arg
|
|
||||||
|
|
||||||
def print_bytecode(self):
|
|
||||||
for i in self.op_range(0, len(self.code)):
|
|
||||||
op = self.code[i]
|
|
||||||
if op in self.opc.hasjabs+self.opc.hasjrel:
|
|
||||||
dest = self.get_target(i, op)
|
|
||||||
print '%i\t%s\t%i' % (i, self.opc.opname[op], dest)
|
|
||||||
else:
|
|
||||||
print '%i\t%s\t' % (i, self.opc.opname[op])
|
|
||||||
|
|
||||||
def first_instr(self, start, end, instr, target=None, exact=True):
|
|
||||||
'''
|
|
||||||
Find the first <instr> in the block from start to end.
|
|
||||||
<instr> is any python bytecode instruction or a list of opcodes
|
|
||||||
If <instr> is an opcode with a target (like a jump), a target
|
|
||||||
destination can be specified which must match precisely if exact
|
|
||||||
is True, or if exact is False, the instruction which has a target
|
|
||||||
closest to <target> will be returned.
|
|
||||||
|
|
||||||
Return index to it or None if not found.
|
|
||||||
'''
|
|
||||||
code = self.code
|
|
||||||
assert(start>=0 and end<=len(code))
|
|
||||||
|
|
||||||
try: None in instr
|
|
||||||
except: instr = [instr]
|
|
||||||
|
|
||||||
pos = None
|
|
||||||
distance = len(code)
|
|
||||||
for i in self.op_range(start, end):
|
|
||||||
op = code[i]
|
|
||||||
if op in instr:
|
|
||||||
if target is None:
|
|
||||||
return i
|
|
||||||
dest = self.get_target(i, op)
|
|
||||||
if dest == target:
|
|
||||||
return i
|
|
||||||
elif not exact:
|
|
||||||
_distance = abs(target - dest)
|
|
||||||
if _distance < distance:
|
|
||||||
distance = _distance
|
|
||||||
pos = i
|
|
||||||
return pos
|
|
||||||
|
|
||||||
def last_instr(self, start, end, instr, target=None, exact=True):
|
|
||||||
'''
|
|
||||||
Find the last <instr> in the block from start to end.
|
|
||||||
<instr> is any python bytecode instruction or a list of opcodes
|
|
||||||
If <instr> is an opcode with a target (like a jump), a target
|
|
||||||
destination can be specified which must match precisely if exact
|
|
||||||
is True, or if exact is False, the instruction which has a target
|
|
||||||
closest to <target> will be returned.
|
|
||||||
|
|
||||||
Return index to it or None if not found.
|
|
||||||
'''
|
|
||||||
|
|
||||||
code = self.code
|
|
||||||
if not (start>=0 and end<=len(code)):
|
|
||||||
return None
|
|
||||||
|
|
||||||
try: None in instr
|
|
||||||
except: instr = [instr]
|
|
||||||
|
|
||||||
pos = None
|
|
||||||
distance = len(code)
|
|
||||||
for i in self.op_range(start, end):
|
|
||||||
op = code[i]
|
|
||||||
if op in instr:
|
|
||||||
if target is None:
|
|
||||||
pos = i
|
|
||||||
else:
|
|
||||||
dest = self.get_target(i, op)
|
|
||||||
if dest == target:
|
|
||||||
distance = 0
|
|
||||||
pos = i
|
|
||||||
elif not exact:
|
|
||||||
_distance = abs(target - dest)
|
|
||||||
if _distance <= distance:
|
|
||||||
distance = _distance
|
|
||||||
pos = i
|
|
||||||
return pos
|
|
||||||
|
|
||||||
def all_instr(self, start, end, instr, target=None, include_beyond_target=False):
|
|
||||||
'''
|
|
||||||
Find all <instr> in the block from start to end.
|
|
||||||
<instr> is any python bytecode instruction or a list of opcodes
|
|
||||||
If <instr> is an opcode with a target (like a jump), a target
|
|
||||||
destination can be specified which must match precisely.
|
|
||||||
|
|
||||||
Return a list with indexes to them or [] if none found.
|
|
||||||
'''
|
|
||||||
|
|
||||||
code = self.code
|
|
||||||
assert(start>=0 and end<=len(code))
|
|
||||||
|
|
||||||
try: None in instr
|
|
||||||
except: instr = [instr]
|
|
||||||
|
|
||||||
result = []
|
|
||||||
for i in self.op_range(start, end):
|
|
||||||
op = code[i]
|
|
||||||
if op in instr:
|
|
||||||
if target is None:
|
|
||||||
result.append(i)
|
|
||||||
else:
|
|
||||||
t = self.get_target(i, op)
|
|
||||||
if include_beyond_target and t >= target:
|
|
||||||
result.append(i)
|
|
||||||
elif t == target:
|
|
||||||
result.append(i)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def op_size(self, op):
|
|
||||||
if op < self.opc.HAVE_ARGUMENT and op not in self.opc.hasArgumentExtended:
|
|
||||||
return 1
|
|
||||||
else:
|
|
||||||
return 3
|
|
||||||
|
|
||||||
def op_hasArgument(self, op):
|
|
||||||
return self.op_size(op) > 1
|
|
||||||
|
|
||||||
def op_range(self, start, end):
|
|
||||||
while start < end:
|
|
||||||
yield start
|
|
||||||
start += self.op_size(self.code[start])
|
|
||||||
|
|
||||||
def remove_mid_line_ifs(self, ifs):
|
|
||||||
filtered = []
|
|
||||||
for i in ifs:
|
|
||||||
if self.lines[i].l_no == self.lines[i+3].l_no:
|
|
||||||
if self.code[self.prev[self.lines[i].next]] in (self.opc.PJIT, self.opc.PJIF):
|
|
||||||
continue
|
|
||||||
filtered.append(i)
|
|
||||||
return filtered
|
|
||||||
|
|
||||||
def rem_or(self, start, end, instr, target=None, include_beyond_target=False):
|
|
||||||
'''
|
|
||||||
Find all <instr> in the block from start to end.
|
|
||||||
<instr> is any python bytecode instruction or a list of opcodes
|
|
||||||
If <instr> is an opcode with a target (like a jump), a target
|
|
||||||
destination can be specified which must match precisely.
|
|
||||||
|
|
||||||
Return a list with indexes to them or [] if none found.
|
|
||||||
'''
|
|
||||||
|
|
||||||
code = self.code
|
|
||||||
assert(start>=0 and end<=len(code))
|
|
||||||
|
|
||||||
try: None in instr
|
|
||||||
except: instr = [instr]
|
|
||||||
|
|
||||||
result = []
|
|
||||||
for i in self.op_range(start, end):
|
|
||||||
op = code[i]
|
|
||||||
if op in instr:
|
|
||||||
if target is None:
|
|
||||||
result.append(i)
|
|
||||||
else:
|
|
||||||
t = self.get_target(i, op)
|
|
||||||
if include_beyond_target and t >= target:
|
|
||||||
result.append(i)
|
|
||||||
elif t == target:
|
|
||||||
result.append(i)
|
|
||||||
|
|
||||||
pjits = self.all_instr(start, end, self.opc.PJIT)
|
|
||||||
filtered = []
|
|
||||||
for pjit in pjits:
|
|
||||||
tgt = self.get_target(pjit)-3
|
|
||||||
for i in result:
|
|
||||||
if i <= pjit or i >= tgt:
|
|
||||||
filtered.append(i)
|
|
||||||
result = filtered
|
|
||||||
filtered = []
|
|
||||||
return result
|
|
||||||
|
|
||||||
def restrict_to_parent(self, target, parent):
|
|
||||||
'''Restrict pos to parent boundaries.'''
|
|
||||||
if not (parent['start'] < target < parent['end']):
|
|
||||||
target = parent['end']
|
|
||||||
return target
|
|
88
uncompyle6/magics.py
Executable file
88
uncompyle6/magics.py
Executable file
@@ -0,0 +1,88 @@
|
|||||||
|
from __future__ import print_function
|
||||||
|
import struct
|
||||||
|
|
||||||
|
__all__ = ['magics', 'versions']
|
||||||
|
|
||||||
|
def __build_magic(magic):
|
||||||
|
return struct.pack('Hcc', magic, '\r', '\n')
|
||||||
|
|
||||||
|
by_magic = {}
|
||||||
|
by_version = {}
|
||||||
|
|
||||||
|
def __by_version(magics):
|
||||||
|
for m, v in magics.items():
|
||||||
|
by_magic[m] = v
|
||||||
|
by_version[v] = m
|
||||||
|
return by_version
|
||||||
|
|
||||||
|
versions = {
|
||||||
|
# taken from from Python/import.c
|
||||||
|
# magic, version
|
||||||
|
__build_magic(20121): '1.5', # 1.5, 1.5.1, 1.5.2
|
||||||
|
__build_magic(50428): '1.6', # 1.6
|
||||||
|
__build_magic(50823): '2.0', # 2.0, 2.0.1
|
||||||
|
__build_magic(60202): '2.1', # 2.1, 2.1.1, 2.1.2
|
||||||
|
__build_magic(60717): '2.2', # 2.2
|
||||||
|
__build_magic(62011): '2.3', # 2.3a0
|
||||||
|
__build_magic(62021): '2.3', # 2.3a0
|
||||||
|
__build_magic(62041): '2.4', # 2.4a0
|
||||||
|
__build_magic(62051): '2.4', # 2.4a3
|
||||||
|
__build_magic(62061): '2.4', # 2.4b1
|
||||||
|
__build_magic(62071): '2.5', # 2.5a0
|
||||||
|
__build_magic(62081): '2.5', # 2.5a0 (ast-branch)
|
||||||
|
__build_magic(62091): '2.5', # 2.5a0 (with)
|
||||||
|
__build_magic(62092): '2.5', # 2.5a0 (changed WITH_CLEANUP opcode)
|
||||||
|
__build_magic(62101): '2.5', # 2.5b3 (fix wrong code: for x, in ...)
|
||||||
|
__build_magic(62111): '2.5', # 2.5b3 (fix wrong code: x += yield)
|
||||||
|
__build_magic(62121): '2.5', # 2.5c1 (fix wrong lnotab with for loops and
|
||||||
|
# storing constants that should have been removed
|
||||||
|
__build_magic(62131): '2.5', # 2.5c2 (fix wrong code: for x, in ... in listcomp/genexp)
|
||||||
|
__build_magic(62151): '2.6', # 2.6a0 (peephole optimizations & STORE_MAP)
|
||||||
|
__build_magic(62161): '2.6', # 2.6a1 (WITH_CLEANUP optimization)
|
||||||
|
__build_magic(62171): '2.7', # 2.7a0 (optimize list comprehensions/change LIST_APPEND)
|
||||||
|
__build_magic(62181): '2.7', # 2.7a0 (optimize conditional branches:
|
||||||
|
# introduce POP_JUMP_IF_FALSE and POP_JUMP_IF_TRUE)
|
||||||
|
__build_magic(62191): '2.7', # 2.7a0 (introduce SETUP_WITH)
|
||||||
|
__build_magic(62201): '2.7', # 2.7a0 (introduce BUILD_SET)
|
||||||
|
__build_magic(62211): '2.7', # 2.7a0 (introduce MAP_ADD and SET_ADD)
|
||||||
|
__build_magic(3000): '3.0', # 3.000
|
||||||
|
__build_magic(3010): '3.0', # 3.000 (removed UNARY_CONVERT)
|
||||||
|
__build_magic(3020): '3.0', # 3.000 (added BUILD_SET)
|
||||||
|
__build_magic(3030): '3.0', # 3.000 (added keyword-only parameters)
|
||||||
|
__build_magic(3040): '3.0', # 3.000 (added signature annotations)
|
||||||
|
__build_magic(3050): '3.0', # 3.000 (print becomes a function)
|
||||||
|
__build_magic(3060): '3.0', # 3.000 (PEP 3115 metaclass syntax)
|
||||||
|
__build_magic(3061): '3.0', # 3.000 (string literals become unicode)
|
||||||
|
__build_magic(3071): '3.0', # 3.000 (PEP 3109 raise changes)
|
||||||
|
__build_magic(3081): '3.0', # 3.000 (PEP 3137 make __file__ and __name__ unicode)
|
||||||
|
__build_magic(3091): '3.0', # 3.000 (kill str8 interning)
|
||||||
|
__build_magic(3101): '3.0', # 3.000 (merge from 2.6a0, see 62151)
|
||||||
|
__build_magic(3103): '3.0', # 3.000 (__file__ points to source file)
|
||||||
|
__build_magic(3111): '3.0', # 3.0a4 (WITH_CLEANUP optimization).
|
||||||
|
__build_magic(3131): '3.0', # 3.0a5 (lexical exception stacking, including POP_EXCEPT)
|
||||||
|
__build_magic(3141): '3.1', # 3.1a0 (optimize list, set and dict comprehensions)
|
||||||
|
__build_magic(3151): '3.1', # 3.1a0 (optimize conditional branches)
|
||||||
|
__build_magic(3160): '3.2', # 3.2a0 (add SETUP_WITH)
|
||||||
|
__build_magic(3170): '3.2', # 3.2a1 (add DUP_TOP_TWO, remove DUP_TOPX and ROT_FOUR)
|
||||||
|
__build_magic(3180): '3.2', # 3.2a2 (add DELETE_DEREF)
|
||||||
|
}
|
||||||
|
|
||||||
|
magics = __by_version(versions)
|
||||||
|
|
||||||
|
def __show(text, magic):
|
||||||
|
print(text, struct.unpack('BBBB', magic), struct.unpack('HBB', magic))
|
||||||
|
|
||||||
|
def test():
|
||||||
|
import imp
|
||||||
|
magic_20 = magics['2.0']
|
||||||
|
current = imp.get_magic()
|
||||||
|
current_version = struct.unpack('HBB', current)[0]
|
||||||
|
magic_current = by_magic[ current ]
|
||||||
|
print(type(magic_20), len(magic_20), repr(magic_20))
|
||||||
|
print()
|
||||||
|
print('This Python interpreter has version', magic_current)
|
||||||
|
print('Magic code: ', current_version)
|
||||||
|
print(type(magic_20), len(magic_20), repr(magic_20))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
test()
|
@@ -26,12 +26,14 @@
|
|||||||
|
|
||||||
%c evaluate N[A] recursively*
|
%c evaluate N[A] recursively*
|
||||||
%C evaluate N[A[0]]..N[A[1]-1] recursively, separate by A[2]*
|
%C evaluate N[A[0]]..N[A[1]-1] recursively, separate by A[2]*
|
||||||
|
%P same as %C but sets operator precedence
|
||||||
%, print ',' if last %C only printed one item (for tuples--unused)
|
%, print ',' if last %C only printed one item (for tuples--unused)
|
||||||
%| tab to current indentation level
|
%| tab to current indentation level
|
||||||
%+ increase current indentation level
|
%+ increase current indentation level
|
||||||
%- decrease current indentation level
|
%- decrease current indentation level
|
||||||
%{...} evaluate ... in context of N
|
%{...} evaluate ... in context of N
|
||||||
%% literal '%'
|
%% literal '%'
|
||||||
|
%p evaluate N setting precedence
|
||||||
|
|
||||||
* indicates an argument (A) required.
|
* indicates an argument (A) required.
|
||||||
|
|
Reference in New Issue
Block a user