You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-02 16:44:46 +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>
|
||||
#
|
||||
'''
|
||||
Usage: uncompyle2 [OPTIONS]... [ FILE | DIR]...
|
||||
Usage: uncompyle6 [OPTIONS]... [ FILE | DIR]...
|
||||
|
||||
Examples:
|
||||
uncompyle2 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
|
||||
uncompyle2 -o /tmp /usr/lib/python1.5 # decompile whole library
|
||||
uncompyle6 foo.pyc bar.pyc # decompile foo.pyc, bar.pyc to stdout
|
||||
uncompyle6 -o . foo.pyc bar.pyc # decompile to ./foo.pyc_dis and ./bar.pyc_dis
|
||||
uncompyle6 -o /tmp /usr/lib/python1.5 # decompile whole library
|
||||
|
||||
Options:
|
||||
-o <path> output decompiled files to this path:
|
||||
@@ -41,15 +41,15 @@ Extensions of generated files:
|
||||
'''
|
||||
|
||||
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 os.path
|
||||
from uncompyle2 import main, verify
|
||||
from uncompyle6 import main, verify
|
||||
import time
|
||||
|
||||
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)
|
||||
|
||||
showasm = showast = do_verify = numproc = recurse_dirs = 0
|
8
setup.py
8
setup.py
@@ -4,12 +4,12 @@
|
||||
|
||||
from distutils.core import setup, Extension
|
||||
|
||||
setup (name = "uncompyle2",
|
||||
version = "1.1",
|
||||
setup (name = "uncompyle6",
|
||||
version = "2.0",
|
||||
description = "Python byte-code to source-code converter",
|
||||
author = "Mysterie",
|
||||
author_email = "kajusska@gmail.com",
|
||||
url = "http://github.com/Mysterie/uncompyle2",
|
||||
packages=['uncompyle2', 'uncompyle2.opcode'],
|
||||
scripts=['scripts/uncompyle2']
|
||||
packages=['uncompyle6', 'uncompyle6.opcode'],
|
||||
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
|
||||
import sys, inspect, types, cStringIO
|
||||
|
||||
from collections import namedtuple
|
||||
NodeInfo = namedtuple("NodeInfo", "node start finish")
|
||||
|
||||
class FindWalker(walker.Walker, object):
|
||||
stacked_params = ('f', 'indent', 'isLambda', '_globals')
|
||||
|
||||
@@ -22,6 +25,10 @@ class FindWalker(walker.Walker, object):
|
||||
self.currentclass = None
|
||||
self.pending_newlines = 0
|
||||
|
||||
self.found_offset = False
|
||||
self.offsets = {}
|
||||
|
||||
|
||||
f = property(lambda s: s.__params['f'],
|
||||
lambda s, x: s.__params.__setitem__('f', x),
|
||||
lambda s: s.__params.__delitem__('f'),
|
||||
@@ -47,10 +54,10 @@ class FindWalker(walker.Walker, object):
|
||||
node = self.ast
|
||||
|
||||
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:
|
||||
self.found_offset = True
|
||||
print 'BINGO!'
|
||||
# print 'BINGO!'
|
||||
|
||||
try:
|
||||
name = 'n_' + self.typestring(node)
|
||||
@@ -60,6 +67,12 @@ class FindWalker(walker.Walker, object):
|
||||
else:
|
||||
self.default(node)
|
||||
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
|
||||
|
||||
for kid in node:
|
||||
@@ -70,6 +83,9 @@ class FindWalker(walker.Walker, object):
|
||||
func = getattr(self, name)
|
||||
func(node)
|
||||
|
||||
return
|
||||
|
||||
|
||||
def find_source(self, offset, ast, customize, isLambda=0, returnNone=False):
|
||||
"""convert AST to source code"""
|
||||
|
||||
@@ -86,10 +102,11 @@ class FindWalker(walker.Walker, object):
|
||||
self.print_(self.indent, 'pass')
|
||||
else:
|
||||
self.customize(customize)
|
||||
result = self.traverse(ast, isLambda=isLambda)
|
||||
if isLambda:
|
||||
self.write(self.traverse(ast, isLambda=isLambda))
|
||||
self.write(result)
|
||||
else:
|
||||
self.print_(self.traverse(ast, isLambda=isLambda))
|
||||
self.print_(result)
|
||||
self.return_none = rn
|
||||
|
||||
# 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)
|
||||
print
|
||||
print '------------------------'
|
||||
uncompyle_find(2.7, co, 24)
|
||||
uncompyle_find(2.7, co, 33)
|
||||
finally:
|
||||
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[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)
|
||||
%| tab to current indentation level
|
||||
%+ increase current indentation level
|
||||
%- decrease current indentation level
|
||||
%{...} evaluate ... in context of N
|
||||
%% literal '%'
|
||||
%p evaluate N setting precedence
|
||||
|
||||
* indicates an argument (A) required.
|
||||
|
Reference in New Issue
Block a user