├── decompyle ├── test │ ├── test_single_stmt.py │ ├── test_empty.py │ ├── test_iterators.py │ ├── test_divide_future.py │ ├── test_divide_no_future.py │ ├── test_exec.py │ ├── test_print.py │ ├── test_extendedPrint.py │ ├── test_globals.py │ ├── test_expressions.py │ ├── test_yield.py │ ├── test_tuple_params.py │ ├── test_loops2.py │ ├── test_tuples.py │ ├── test_print_to.py │ ├── test_import.py │ ├── test_lambda.py │ ├── test_global.py │ ├── test_extendedImport.py │ ├── test_import_as.py │ ├── test_del.py │ ├── test_integers.py │ ├── test_docstring.py │ ├── test_class.py │ ├── test_applyEquiv.py │ ├── test_loops.py │ ├── test_misc.py │ ├── test_slices.py │ ├── test_listComprehensions.py │ ├── test_augmentedAssign.py │ ├── test_functions.py │ ├── test_nested_elif.py │ ├── test_nested_scopes.py │ ├── test_exceptions.py │ ├── compile_tests │ └── test_prettyprint.py ├── test_one ├── TODO ├── setup.py ├── decompyle │ ├── marshal_files.py │ ├── dis_files.py │ ├── magics.py │ ├── opcode_23.py │ ├── opcode_25.py │ ├── dis_15.py │ ├── dis_23.py │ ├── Scanner.py │ ├── dis_25.py │ ├── verify.py │ ├── dis_16.py │ ├── __init__.py │ ├── dis_20.py │ ├── dis_21.py │ ├── dis_22.py │ ├── optimizer.py │ ├── new-Parser.py │ └── spark.py ├── scripts │ └── decompyle ├── test_pythonlib ├── CHANGES └── README ├── tests ├── 04block.rb ├── 03at.rb ├── 02array.rb ├── 05init.rb ├── 01puts.rb ├── 07if.rb └── all ├── bin ├── py-compile ├── unholy ├── rb-compile └── py-dis ├── lib ├── unholy │ ├── tuple.rb │ ├── pickle.rb │ └── pyasm.rb └── unholy.rb ├── python └── Kernel.py ├── COPYING └── README /decompyle/test/test_single_stmt.py: -------------------------------------------------------------------------------- 1 | print 5 2 | -------------------------------------------------------------------------------- /decompyle/test/test_empty.py: -------------------------------------------------------------------------------- 1 | # tis is an empty file 2 | -------------------------------------------------------------------------------- /tests/04block.rb: -------------------------------------------------------------------------------- 1 | # 3 2 | a = proc { |x| x + 1 } 3 | puts a.call(2) 4 | -------------------------------------------------------------------------------- /tests/03at.rb: -------------------------------------------------------------------------------- 1 | # 4 2 | # 5 3 | a = [1, 2, 3, 4, 5, 6] 4 | print a[3] 5 | print a[-2] 6 | -------------------------------------------------------------------------------- /bin/py-compile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import py_compile, sys 3 | py_compile.compile(sys.argv[1]) 4 | -------------------------------------------------------------------------------- /tests/02array.rb: -------------------------------------------------------------------------------- 1 | # [1, 2, 'c', 'd', [3, 4]] 2 | a = [ 1, 2 ] << "c" << "d" << [ 3, 4 ] 3 | print a 4 | -------------------------------------------------------------------------------- /bin/unholy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $: << "./lib" 3 | require 'unholy' 4 | 5 | unholy(ARGV[0], ARGV[0] + ".pyc") 6 | -------------------------------------------------------------------------------- /tests/05init.rb: -------------------------------------------------------------------------------- 1 | # init 2 | class Smalls 3 | def initialize 4 | puts "init" 5 | end 6 | end 7 | 8 | Smalls.new 9 | -------------------------------------------------------------------------------- /tests/01puts.rb: -------------------------------------------------------------------------------- 1 | # Content-Type: text/plain 2 | # 3 | # Hello, world! 4 | puts "Content-Type: text/plain" 5 | puts 6 | puts "Hello, world!" 7 | -------------------------------------------------------------------------------- /bin/rb-compile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | iseq = VM::InstructionSequence.compile(IO.read(ARGV[0])) 3 | iseq.to_a.last.each do |inst| 4 | p inst 5 | end 6 | -------------------------------------------------------------------------------- /lib/unholy/tuple.rb: -------------------------------------------------------------------------------- 1 | class Tuple < Array 2 | def to_pickle 3 | "(" + length.to_plong + map { |x| x.to_pickle }.join 4 | end 5 | end 6 | 7 | def tuple(*ary); Tuple[*ary] end 8 | -------------------------------------------------------------------------------- /decompyle/test/test_iterators.py: -------------------------------------------------------------------------------- 1 | 2 | for i in range(20): 3 | print i, 4 | 5 | print 6 | 7 | for i in range(10): 8 | print i, 9 | #if i == 10: break 10 | else: 11 | print 'The End' 12 | -------------------------------------------------------------------------------- /decompyle/test/test_divide_future.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | 3 | print ' 1 // 2 =', 1 // 2 4 | print '1.0 // 2.0 =', 1.0 // 2.0 5 | print ' 1 / 2 =', 1 / 2 6 | print '1.0 / 2.0 =', 1.0 / 2.0 7 | -------------------------------------------------------------------------------- /decompyle/test/test_divide_no_future.py: -------------------------------------------------------------------------------- 1 | #from __future__ import division 2 | 3 | print ' 1 // 2 =', 1 // 2 4 | print '1.0 // 2.0 =', 1.0 // 2.0 5 | print ' 1 / 2 =', 1 / 2 6 | print '1.0 / 2.0 =', 1.0 / 2.0 7 | -------------------------------------------------------------------------------- /lib/unholy.rb: -------------------------------------------------------------------------------- 1 | require 'unholy/tuple' 2 | require 'unholy/pickle' 3 | require 'unholy/pyasm' 4 | 5 | def unholy(fname, fpyc) 6 | asm = Pyasm.new(fname) 7 | asm.import :Kernel 8 | asm.eval IO.read(fname) 9 | asm.compile(fpyc) 10 | end 11 | -------------------------------------------------------------------------------- /tests/07if.rb: -------------------------------------------------------------------------------- 1 | # 123 2 | # 123 3 | # 456 4 | # 123 5 | a = [] 6 | if true 7 | a << 123 8 | end 9 | puts a[0] 10 | 11 | a = [] 12 | if true 13 | a << 123 14 | else 15 | a << 456 16 | end 17 | puts a[0] 18 | 19 | a = [] 20 | if false 21 | a << 123 22 | else 23 | a << 456 24 | end 25 | puts a[0] 26 | 27 | puts 123 if true 28 | -------------------------------------------------------------------------------- /python/Kernel.py: -------------------------------------------------------------------------------- 1 | class Proc: 2 | def __init__(self, code): 3 | self.code = code 4 | def call(self, *args): 5 | return eval(self.code, dict(zip(self.code.co_varnames, args))) 6 | 7 | def proc(func): 8 | return Proc(func) 9 | 10 | def puts(*args): 11 | for x in args: print x 12 | if not args: print 13 | 14 | class BasicObject: 15 | pass 16 | -------------------------------------------------------------------------------- /decompyle/test_one: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | file=$1 4 | shift 5 | options=$@ 6 | 7 | BASEDIR=test/bytecode_1.5 8 | #BASEDIR=test/bytecode_2.0 9 | #BASEDIR=test/bytecode_2.1 10 | #BASEDIR=test/bytecode_2.2 11 | 12 | if [ `dirname $file` == '.' ] ; then 13 | file=$BASEDIR/test_$file.pyc 14 | fi 15 | 16 | python2 -u ./scripts/decompyle $options $file 2>&1 |less 17 | -------------------------------------------------------------------------------- /decompyle/test/test_exec.py: -------------------------------------------------------------------------------- 1 | # exec.py -- source test pattern for exec statement 2 | # 3 | # This simple program is part of the decompyle test suite. 4 | # 5 | # decompyle is a Python byte-code decompiler 6 | # See http://www.goebel-consult.de/decompyle/ for download and 7 | # for further information 8 | 9 | testcode = 'a = 12' 10 | 11 | exec testcode 12 | exec testcode in globals() 13 | exec testcode in globals(), locals() 14 | -------------------------------------------------------------------------------- /decompyle/test/test_print.py: -------------------------------------------------------------------------------- 1 | # print.py -- source test pattern for print statements 2 | # 3 | # This simple program is part of the decompyle test suite. 4 | # 5 | # decompyle is a Python byte-code decompiler 6 | # See http://www.goebel-consult.de/decompyle/ for download and 7 | # for further information 8 | 9 | print 1,2,3,4,5 10 | a = b + 5 11 | print 1,2,3,4,5 12 | print 1,2,3,4,5 13 | print 14 | print 15 | print 1,2,3,4,5 16 | print 17 | -------------------------------------------------------------------------------- /lib/unholy/pickle.rb: -------------------------------------------------------------------------------- 1 | class Module 2 | def to_pickle; self.name.to_pickle end 3 | end 4 | 5 | class NilClass 6 | def to_pickle; "N" end 7 | end 8 | 9 | class Integer 10 | def to_plong; [self].pack("L") end 11 | def to_pickle; "i#{to_plong}" end 12 | end 13 | 14 | class String 15 | def to_pickle; "s" + length.to_plong + self end 16 | end 17 | 18 | class Symbol 19 | def to_pickle 20 | str = to_s 21 | "t" + str.length.to_plong + str 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /decompyle/test/test_extendedPrint.py: -------------------------------------------------------------------------------- 1 | # extendedPrint.py -- source test pattern for extended print statements 2 | # 3 | # This simple program is part of the decompyle test suite. 4 | # 5 | # decompyle is a Python byte-code decompiler 6 | # See http://www.goebel-consult.de/decompyle/ for download and 7 | # for further information 8 | 9 | import sys 10 | 11 | print >> sys.stdout, "Hello World" 12 | print >> sys.stdout, 1,2,3 13 | print >> sys.stdout, 1,2,3, 14 | print >> sys.stdout 15 | -------------------------------------------------------------------------------- /decompyle/test/test_globals.py: -------------------------------------------------------------------------------- 1 | # globals.py -- test for global symbols 2 | # 3 | # This simple program is part of the decompyle test suite. 4 | # 5 | # decompyle is a Python byte-code decompiler 6 | # See http://www.goebel-consult.de/decompyle/ for download and 7 | # for further information 8 | 9 | def f(): 10 | print x # would result in a 'NameError' or 'UnboundLocalError' 11 | x = x+1 12 | print x 13 | 14 | raise "This program can't be run" 15 | 16 | x = 1 17 | f() 18 | print x 19 | -------------------------------------------------------------------------------- /decompyle/test/test_expressions.py: -------------------------------------------------------------------------------- 1 | # expressions.py -- source test pattern for expressions 2 | # 3 | # This simple program is part of the decompyle test suite. 4 | # 5 | # decompyle is a Python byte-code decompiler 6 | # See http://www.goebel-consult.de/decompyle/ for download and 7 | # for further information 8 | 9 | def _lsbStrToInt(str): 10 | return ord(str[0]) + \ 11 | (ord(str[1]) << 8) + \ 12 | (ord(str[2]) << 16) + \ 13 | (ord(str[3]) << 24) 14 | -------------------------------------------------------------------------------- /decompyle/test/test_yield.py: -------------------------------------------------------------------------------- 1 | from __future__ import generators 2 | 3 | def inorder(t): 4 | if t: 5 | for x in inorder(t.left): 6 | yield x 7 | yield t.label 8 | for x in inorder(t.right): 9 | yield x 10 | 11 | def generate_ints(n): 12 | for i in range(n): 13 | yield i*2 14 | 15 | for i in generate_ints(5): 16 | print i, 17 | print 18 | gen = generate_ints(3) 19 | print gen.next(), 20 | print gen.next(), 21 | print gen.next(), 22 | print gen.next() 23 | -------------------------------------------------------------------------------- /tests/all: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $: << "./lib" 3 | require 'unholy' 4 | 5 | ENV['PYTHONPATH'] = 'python' 6 | 7 | `rm -rf tests/*.pyc` 8 | Dir["tests/*.rb"].each do |rb| 9 | unholy(rb, rb + ".pyc") 10 | expect = File.read(rb).scan(/^# (.+\n)|^#(\n)/).join 11 | actual = `python #{rb}.pyc` 12 | puts "#{rb}: #{expect == actual ? "passed" : "FAILED"}" 13 | unless expect == actual 14 | puts " expected:" 15 | puts expect.gsub(/^/, ' ') 16 | puts " actual:" 17 | puts actual.gsub(/^/, ' ') 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /decompyle/test/test_tuple_params.py: -------------------------------------------------------------------------------- 1 | """ 2 | test_tuple_params.py -- source test pattern for formal parameters of type tuple 3 | 4 | This source is part of the decompyle test suite. 5 | 6 | decompyle is a Python byte-code decompiler 7 | See http://www.goebel-consult.de/decompyle/ for download and 8 | for further information 9 | """ 10 | 11 | def A(a,b,(x,y,z),c): 12 | pass 13 | 14 | def B(a,b=42,(x,y,z)=(1,2,3),c=17): 15 | pass 16 | 17 | def C((x,y,z)): 18 | pass 19 | 20 | def D((x,)): 21 | pass 22 | 23 | def E((x)): 24 | pass 25 | -------------------------------------------------------------------------------- /decompyle/test/test_loops2.py: -------------------------------------------------------------------------------- 1 | """ 2 | test_loops2.py -- source test pattern for loops (CONTINUE_LOOP) 3 | 4 | This source is part of the decompyle test suite. 5 | 6 | decompyle is a Python byte-code decompiler 7 | See http://www.goebel-consult.de/decompyle/ for download and 8 | for further information 9 | """ 10 | 11 | # This is a seperate test pattern, since 'continue' within 'try' 12 | # was not allowed till Python 2.1 13 | 14 | for term in args: 15 | try: 16 | print 17 | continue 18 | print 19 | except: 20 | pass 21 | -------------------------------------------------------------------------------- /decompyle/test/test_tuples.py: -------------------------------------------------------------------------------- 1 | """ 2 | test_tuples.py -- source test pattern for tuples 3 | 4 | This source is part of the decompyle test suite. 5 | 6 | decompyle is a Python byte-code decompiler 7 | See http://www.goebel-consult.de/decompyle/ for download and 8 | for further information 9 | """ 10 | 11 | a = (1,) 12 | b = (2,3) 13 | a,b = (1,2) 14 | a,b = ( (1,2), (3,4,5) ) 15 | 16 | x = {} 17 | try: 18 | x[1,2,3] 19 | except: 20 | pass 21 | x[1,2,3] = 42 22 | print x[1,2,3] 23 | print x[(1,2,3)] 24 | assert x[(1,2,3)] == x[1,2,3] 25 | del x[1,2,3] 26 | -------------------------------------------------------------------------------- /decompyle/test/test_print_to.py: -------------------------------------------------------------------------------- 1 | """ 2 | print_to.py -- source test pattern for 'print >> ...' statements 3 | 4 | This source is part of the decompyle test suite. 5 | 6 | decompyle is a Python byte-code decompiler 7 | See http://www.goebel-consult.de/decompyle/ for download and 8 | for further information 9 | """ 10 | import sys 11 | 12 | print >>sys.stdout, 1,2,3,4,5 13 | 14 | print >>sys.stdout, 1,2,3,4,5, 15 | print >>sys.stdout 16 | 17 | print >>sys.stdout, 1,2,3,4,5, 18 | print >>sys.stdout, 1,2,3,4,5, 19 | print >>sys.stdout 20 | 21 | print >>sys.stdout 22 | -------------------------------------------------------------------------------- /decompyle/test/test_import.py: -------------------------------------------------------------------------------- 1 | """ 2 | test_import.py -- source test pattern for import statements 3 | 4 | This source is part of the decompyle test suite. 5 | 6 | decompyle is a Python byte-code decompiler 7 | See http://www.goebel-consult.de/decompyle/ for download and 8 | for further information 9 | """ 10 | 11 | import sys 12 | import os, sys, BaseHTTPServer 13 | 14 | import test.test_MimeWriter 15 | 16 | from rfc822 import Message 17 | from mimetools import Message, decode, choose_boundary 18 | from os import * 19 | 20 | for k, v in globals().items(): 21 | print `k`, v 22 | -------------------------------------------------------------------------------- /decompyle/TODO: -------------------------------------------------------------------------------- 1 | TODO-list decompyle 2 | 3 | For Release 2.2: 4 | easy: 5 | - add options for tabwidth, TAB, etc. 6 | - Check whether decompyle can read 1.6 and 2.0 bytecode 7 | medium: 8 | - find out how to verify optimized bytecode (.pyo) 9 | - extend test_nested_scopes with parts form cgitb.html() 10 | - test EXTENDED_ARG -> how? 11 | hard: 12 | - try to find a way to verify 'for ...' if byte-code is generated by 13 | older versions of Python. 14 | 15 | Planned for future releases: 16 | - use weak references in Parser to save memory (maybe) 17 | - avoid unnecessary parentesis in expressions 18 | -------------------------------------------------------------------------------- /decompyle/test/test_lambda.py: -------------------------------------------------------------------------------- 1 | # lambda.py -- source test pattern for lambda functions 2 | # 3 | # This simple program is part of the decompyle test suite. 4 | # 5 | # decompyle is a Python byte-code decompiler 6 | # See http://www.goebel-consult.de/decompyle/ for download and 7 | # for further information 8 | 9 | palette = map(lambda a: (a,a,a), range(256)) 10 | palette = map(lambda (r,g,b): chr(r)+chr(g)+chr(b), palette) 11 | palette = map(lambda r: r, palette) 12 | 13 | palette = lambda (r,g,b,): r 14 | palette = lambda (r): r 15 | palette = lambda r: r 16 | palette = lambda (r): r, palette 17 | -------------------------------------------------------------------------------- /decompyle/test/test_global.py: -------------------------------------------------------------------------------- 1 | """ 2 | test_global.py -- source test pattern for 'global' statement 3 | 4 | This source is part of the decompyle test suite. 5 | 6 | decompyle is a Python byte-code decompiler 7 | See http://www.goebel-consult.de/decompyle/ for download and 8 | for further information 9 | """ 10 | 11 | i = 1; j = 7 12 | def a(): 13 | def b(): 14 | def c(): 15 | k = 34 16 | global i 17 | i = i+k 18 | l = 42 19 | c() 20 | global j 21 | j = j+l 22 | b() 23 | print i, j # should print 35, 49 24 | 25 | a() 26 | print i, j 27 | -------------------------------------------------------------------------------- /decompyle/test/test_extendedImport.py: -------------------------------------------------------------------------------- 1 | # extendedImport.py -- source test pattern for extended import statements 2 | # 3 | # This simple program is part of the decompyle test suite. 4 | # 5 | # decompyle is a Python byte-code decompiler 6 | # See http://www.goebel-consult.de/decompyle/ for download and 7 | # for further information 8 | 9 | import os, sys as System, time 10 | import sys 11 | 12 | from rfc822 import Message as Msg822 13 | from mimetools import Message as MimeMsg, decode, choose_boundary as MimeBoundary 14 | 15 | import test.test_StringIO as StringTest 16 | 17 | for k, v in globals().items(): 18 | print `k`, v 19 | -------------------------------------------------------------------------------- /decompyle/test/test_import_as.py: -------------------------------------------------------------------------------- 1 | """ 2 | test_import_as.py -- source test pattern for 'import .. as 'statements 3 | 4 | This source is part of the decompyle test suite. 5 | 6 | decompyle is a Python byte-code decompiler 7 | See http://www.goebel-consult.de/decompyle/ for download and 8 | for further information 9 | """ 10 | 11 | import sys as SYS 12 | import os as OS, sys as SYSTEM, BaseHTTPServer as HTTPServ 13 | 14 | import test.test_MimeWriter as Mime_Writer 15 | 16 | from rfc822 import Message as MSG 17 | from mimetools import Message as mimeMsg, decode, \ 18 | choose_boundary as mimeBoundry 19 | 20 | print '---' * 20 21 | 22 | for k, v in globals().items(): 23 | print k, repr(v) 24 | -------------------------------------------------------------------------------- /decompyle/test/test_del.py: -------------------------------------------------------------------------------- 1 | """ 2 | test_del.py -- source test pattern for 'del' statements 3 | 4 | This source is part of the decompyle test suite. 5 | Snippet taken from python libs's test_class.py 6 | 7 | decompyle is a Python byte-code decompiler 8 | See http://www.goebel-consult.de/decompyle/ for download and 9 | for further information 10 | """ 11 | 12 | raise "This program can't be run" 13 | 14 | print 0 15 | a = b[5] 16 | print 1 17 | del a 18 | print 2 19 | del b[5] 20 | print 3 21 | 22 | del testme[1] 23 | print 4 24 | del testme[:] 25 | print '4a' 26 | del testme[:42] 27 | print '4b' 28 | del testme[40:42] 29 | print 5 30 | del testme[2:1024:10] 31 | print '5a' 32 | del testme[40,41,42] 33 | print 6 34 | del testme[:42, ..., :24:, 24, 100] 35 | print 7 36 | -------------------------------------------------------------------------------- /decompyle/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """Setup script for the 'decompyle' distribution.""" 4 | 5 | ____revision__ = "$Id: setup.py,v 1.1.1.1 2004/12/14 12:29:31 dan Exp $" 6 | 7 | from distutils.core import setup, Extension 8 | 9 | setup (name = "decompyle", 10 | version = "2.3", 11 | description = "Python byte-code to source-code converter", 12 | author = "Hartmut Goebel", 13 | author_email = "hartmut@oberon.noris.de", 14 | url = "http://www.goebel-consult.de/decompyle/", 15 | packages=['decompyle'], 16 | scripts=['scripts/decompyle'], 17 | ext_modules = [Extension('decompyle/marshal_25', 18 | ['decompyle/marshal_25.c'], 19 | define_macros=[]), 20 | ] 21 | ) 22 | -------------------------------------------------------------------------------- /decompyle/test/test_integers.py: -------------------------------------------------------------------------------- 1 | """ 2 | test_integers.py -- source test pattern for integers 3 | 4 | This source is part of the decompyle test suite. 5 | Snippet taken from python libs's test_class.py 6 | 7 | decompyle is a Python byte-code decompiler 8 | See http://www.goebel-consult.de/decompyle/ for download and 9 | for further information 10 | """ 11 | 12 | import sys 13 | #raise "This program can't be run" 14 | 15 | i = 1 16 | i = 42 17 | i = -1 18 | i = -42 19 | i = sys.maxint 20 | minint = -sys.maxint-1 21 | print sys.maxint 22 | print minint 23 | print long(minint)-1 24 | 25 | print 26 | i = -2147483647 # == -maxint 27 | print i, repr(i) 28 | i = i-1 29 | print i, repr(i) 30 | i = -2147483648L # == minint == -maxint-1 31 | print i, repr(i) 32 | i = -2147483649L # == minint-1 == -maxint-2 33 | print i, repr(i) 34 | -------------------------------------------------------------------------------- /decompyle/test/test_docstring.py: -------------------------------------------------------------------------------- 1 | # docstring.py -- source test pattern for doc strings 2 | # 3 | # This simple program is part of the decompyle test suite. 4 | # 5 | # decompyle is a Python byte-code decompiler 6 | # See http://www.goebel-consult.de/decompyle/ for download and 7 | # for further information 8 | 9 | ''' 10 | This is a doc string 11 | ''' 12 | 13 | def Doc_Test(): 14 | """This has to be present""" 15 | 16 | class XXX: 17 | def __init__(self): 18 | """__init__: This has to be present""" 19 | self.a = 1 20 | 21 | def XXX22(): 22 | """XXX22: This has to be present""" 23 | pass 24 | 25 | def XXX11(): 26 | """XXX22: This has to be present""" 27 | pass 28 | 29 | def XXX12(): 30 | foo = """XXX22: This has to be present""" 31 | pass 32 | 33 | def XXX13(): 34 | pass 35 | 36 | def Y11(): 37 | def Y22(): 38 | def Y33(): 39 | pass 40 | 41 | print __doc__ 42 | -------------------------------------------------------------------------------- /decompyle/test/test_class.py: -------------------------------------------------------------------------------- 1 | """ 2 | test_class.py -- source test pattern for class definitions 3 | 4 | This source is part of the decompyle test suite. 5 | 6 | decompyle is a Python byte-code decompiler 7 | See http://www.goebel-consult.de/decompyle/ for download and 8 | for further information 9 | """ 10 | 11 | class A: 12 | 13 | class A1: 14 | def __init__(self): 15 | print 'A1.__init__' 16 | 17 | def foo(self): 18 | print 'A1.foo' 19 | 20 | def __init__(self): 21 | print 'A.__init__' 22 | 23 | def foo(self): 24 | print 'A.foo' 25 | 26 | 27 | class B: 28 | def __init__(self): 29 | print 'B.__init__' 30 | 31 | def bar(self): 32 | print 'B.bar' 33 | 34 | 35 | class C(A,B): 36 | def foobar(self): 37 | print 'C.foobar' 38 | 39 | 40 | c = C() 41 | c.foo() 42 | c.bar() 43 | c.foobar() 44 | -------------------------------------------------------------------------------- /decompyle/test/test_applyEquiv.py: -------------------------------------------------------------------------------- 1 | # applyEquiv.py -- source test pattern for equivalents of 'apply' 2 | # 3 | # This simple program is part of the decompyle test suite. 4 | # 5 | # decompyle is a Python byte-code decompiler 6 | # See http://www.goebel-consult.de/decompyle/ for download and 7 | # for further information 8 | 9 | def kwfunc(**kwargs): 10 | print kwargs.items() 11 | 12 | def argsfunc(*args): 13 | print args 14 | 15 | def no_apply(*args, **kwargs): 16 | print args 17 | print kwargs.items() 18 | argsfunc(34) 19 | foo = argsfunc(*args) 20 | argsfunc(*args) 21 | argsfunc(34, *args) 22 | kwfunc(**kwargs) 23 | kwfunc(x=11, **kwargs) 24 | no_apply(*args, **kwargs) 25 | no_apply(34, *args, **kwargs) 26 | no_apply(x=11, *args, **kwargs) 27 | no_apply(34, x=11, *args, **kwargs) 28 | no_apply(42, 34, x=11, *args, **kwargs) 29 | 30 | no_apply(1,2,4,8,a=2,b=3,c=5) 31 | -------------------------------------------------------------------------------- /decompyle/test/test_loops.py: -------------------------------------------------------------------------------- 1 | """ 2 | test_loops.py -- source test pattern for loops 3 | 4 | This source is part of the decompyle test suite. 5 | 6 | decompyle is a Python byte-code decompiler 7 | See http://www.goebel-consult.de/decompyle/ for download and 8 | for further information 9 | """ 10 | 11 | for i in range(10): 12 | if i == 3: 13 | continue 14 | if i == 5: 15 | break 16 | print i, 17 | else: 18 | print 'Else' 19 | print 20 | 21 | for i in range(10): 22 | if i == 3: 23 | continue 24 | print i, 25 | else: 26 | print 'Else' 27 | 28 | 29 | i = 0 30 | while i < 10: 31 | i = i+1 32 | if i == 3: 33 | continue 34 | if i == 5: 35 | break 36 | print i, 37 | else: 38 | print 'Else' 39 | print 40 | 41 | i = 0 42 | while i < 10: 43 | i = i+1 44 | if i == 3: 45 | continue 46 | print i, 47 | else: 48 | print 'Else' 49 | -------------------------------------------------------------------------------- /decompyle/test/test_misc.py: -------------------------------------------------------------------------------- 1 | # slices.py -- source test pattern for slices 2 | # 3 | # This simple program is part of the decompyle test suite. 4 | # Snippet taken from python libs's test_class.py 5 | # 6 | # decompyle is a Python byte-code decompiler 7 | # See http://www.goebel-consult.de/decompyle/ for download and 8 | # for further information 9 | 10 | raise "This program can't be run" 11 | 12 | class A: 13 | def __init__(self, num): 14 | self.num = num 15 | def __repr__(self): 16 | return str(self.num) 17 | 18 | b = [] 19 | for i in range(10): 20 | b.append(A(i)) 21 | 22 | for i in ('CALL_FUNCTION', 'CALL_FUNCTION_VAR', 23 | 'CALL_FUNCTION_VAR_KW', 'CALL_FUNCTION_KW'): 24 | print i, '\t', len(i), len(i)-len('CALL_FUNCTION'), 25 | print (len(i)-len('CALL_FUNCTION')) / 3, 26 | print i[len('CALL_FUNCTION'):] 27 | 28 | p2 = (0, 0, None) 29 | if p2[2]: 30 | print 'has value' 31 | else: 32 | print ' no value' 33 | -------------------------------------------------------------------------------- /decompyle/decompyle/marshal_files.py: -------------------------------------------------------------------------------- 1 | import magics 2 | 3 | __all__ = ['by_version', 'by_magic'] 4 | 5 | by_version = { 6 | '1.5': 'marshal_20', 7 | '1.6': 'marshal_20', 8 | '2.0': 'marshal_20', 9 | '2.1': 'marshal_22', 10 | '2.2': 'marshal_22', 11 | '2.3': 'marshal_23', 12 | } 13 | 14 | by_magic = dict( [ (mag, by_version[ver]) 15 | for mag, ver in magics.versions.iteritems() ] ) 16 | 17 | def import_(module=None,version=None,magic=None): 18 | if module: pass 19 | elif version: module = by_version[version] 20 | elif magic: module = by_magic[magic] 21 | else: 22 | raise 'at least one argument is required' 23 | from __builtin__ import __import__ 24 | if module == 'marshal': 25 | # use current version's 'marshal' module 26 | return __import__('marshal', globals(), locals()) 27 | else: 28 | return __import__('decompyle.%s' % module, globals(), 29 | locals(), 'decompyle') 30 | 31 | -------------------------------------------------------------------------------- /decompyle/test/test_slices.py: -------------------------------------------------------------------------------- 1 | """ 2 | test_slices.py -- source test pattern for slices 3 | 4 | This source is part of the decompyle test suite. 5 | Snippet taken from python libs's test_class.py 6 | 7 | decompyle is a Python byte-code decompiler 8 | See http://www.goebel-consult.de/decompyle/ for download and 9 | for further information 10 | """ 11 | 12 | raise "This program can't be run" 13 | 14 | testme[1] 15 | testme[1] = 1 16 | del testme[1] 17 | 18 | testme[:42] 19 | testme[:42] = "The Answer" 20 | del testme[:42] 21 | 22 | testme[2:1024:] 23 | testme[:1024:10] 24 | testme[::] 25 | testme[2:1024:10] 26 | testme[2:1024:10] = "A lot" 27 | del testme[2:1024:10] 28 | 29 | testme[:42, ..., :24:, 24, 100] 30 | testme[:42, ..., :24:, 24, 100] = "Strange" 31 | del testme[:42, ..., :24:, 24, 100] 32 | 33 | testme[:] 34 | testme[:] = 'Take all' 35 | del testme[:] 36 | 37 | testme[40:42] 38 | testme[40:42] = 'Three' 39 | del testme[40:42] 40 | 41 | testme[40,41,42] 42 | testme[40,41,42] = 'Another Three' 43 | del testme[40,41,42] 44 | -------------------------------------------------------------------------------- /decompyle/test/test_listComprehensions.py: -------------------------------------------------------------------------------- 1 | # listComprehensions.py -- source test pattern for list comprehensions 2 | # 3 | # This simple program is part of the decompyle test suite. 4 | # 5 | # decompyle is a Python byte-code decompiler 6 | # See http://www.goebel-consult.de/decompyle/ for download and 7 | # for further information 8 | 9 | XXX = range(4) 10 | 11 | print [i for i in XXX] 12 | print 13 | print [i for i in (1,2,3,4,)] 14 | print 15 | print [(i,1) for i in XXX] 16 | print 17 | print [i*2 for i in range(4)] 18 | print 19 | print [i*j for i in range(4) 20 | for j in range(7)] 21 | print [i*2 for i in range(4) if i == 0 ] 22 | print [(i,i**2) for i in range(4) if (i % 2) == 0 ] 23 | print [i*j for i in range(4) 24 | if i == 2 25 | for j in range(7) 26 | if (i+i % 2) == 0 ] 27 | 28 | seq1 = 'abc' 29 | seq2 = (1,2,3) 30 | 31 | [ (x,y) for x in seq1 for y in seq2 ] 32 | 33 | def flatten(seq): 34 | return [x for subseq in seq for x in subseq] 35 | 36 | print flatten([[0], [1,2,3], [4,5], [6,7,8,9], []]) 37 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008 why the lucky stiff 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without restriction, 6 | including without limitation the rights to use, copy, modify, merge, 7 | publish, distribute, sublicense, and/or sell copies of the Software, 8 | and to permit persons to whom the Software is furnished to do so, 9 | subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 15 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 16 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 17 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 18 | SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 20 | OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /decompyle/test/test_augmentedAssign.py: -------------------------------------------------------------------------------- 1 | """ 2 | augmentedAssign.py -- source test pattern for augmented assigns 3 | 4 | This source is part of the decompyle test suite. 5 | 6 | decompyle is a Python byte-code decompiler 7 | See http://www.goebel-consult.de/decompyle/ for download and 8 | for further information 9 | """ 10 | 11 | raise "This program can't be run" 12 | 13 | a = 1 14 | b = 2 15 | a += b; print a # a = a+b = 3 16 | a -= b; print a # a = a-b = 1 17 | a *= b; print a # a = a*b = 2 18 | a -= a; print a # a = a-a = 0 19 | a += 7*3; print a # == 21 20 | 21 | l= [1,2,3] 22 | l[1] *= 3; print l[1]; # 6 23 | l[1][2][3] = 7 24 | l[1][2][3] *= 3; 25 | l[:] += [9]; print l 26 | l[:2] += [9]; print l 27 | l[1:] += [9]; print l 28 | l[1:4] += [9]; print l 29 | l += [42,43]; print l 30 | 31 | a.value = 1 32 | a.value += 1; 33 | a.b.val = 1 34 | a.b.val += 1; 35 | 36 | l = [] 37 | for i in range(3): 38 | lj = [] 39 | for j in range(3): 40 | lk = [] 41 | for k in range(3): 42 | lk.append(0) 43 | lj.append(lk) 44 | l.append(lj) 45 | 46 | i = j = k = 1 47 | def f(): 48 | global i 49 | i += 1 50 | return i 51 | 52 | l[i][j][k] = 1 53 | i = 1 54 | l[f()][j][k] += 1 55 | print i, l 56 | -------------------------------------------------------------------------------- /decompyle/test/test_functions.py: -------------------------------------------------------------------------------- 1 | # test_functions.py -- source test pattern for functions 2 | # 3 | # This source is part of the decompyle test suite. 4 | # 5 | # decompyle is a Python byte-code decompiler 6 | # See http://www.goebel-consult.de/decompyle/ for download and 7 | # for further information 8 | 9 | def x0(): 10 | pass 11 | 12 | def x1(arg1): 13 | pass 14 | 15 | def x2(arg1,arg2): 16 | pass 17 | 18 | def x3a(*args): 19 | pass 20 | 21 | def x3b(**kwargs): 22 | pass 23 | 24 | def x3c(*args, **kwargs): 25 | pass 26 | 27 | def x4a(foo, bar=1, bla=2, *args): 28 | pass 29 | 30 | def x4b(foo, bar=1, bla=2, **kwargs): 31 | pass 32 | 33 | def x4c(foo, bar=1, bla=2, *args, **kwargs): 34 | pass 35 | 36 | def func_with_tuple_args((a,b)): 37 | print a 38 | print b 39 | 40 | def func_with_tuple_args2((a,b), (c,d)): 41 | print a 42 | print c 43 | 44 | def func_with_tuple_args3((a,b), (c,d), *args): 45 | print a 46 | print c 47 | 48 | def func_with_tuple_args4((a,b), (c,d), **kwargs): 49 | print a 50 | print c 51 | 52 | def func_with_tuple_args5((a,b), (c,d), *args, **kwargs): 53 | print a 54 | print c 55 | 56 | def func_with_tuple_args6((a,b), (c,d)=(2,3), *args, **kwargs): 57 | print a 58 | print c 59 | -------------------------------------------------------------------------------- /decompyle/decompyle/dis_files.py: -------------------------------------------------------------------------------- 1 | import magics 2 | 3 | __all__ = ['by_version', 'by_magic'] 4 | 5 | _fallback = { 6 | 'EXTENDED_ARG': None, 7 | 'hasfree': [], 8 | } 9 | 10 | class dis(object): 11 | def __init__(self, version, module): 12 | self._version = version 13 | from __builtin__ import __import__ 14 | self._module = __import__('decompyle.%s' % module, globals(), 15 | locals(), 'decompyle') 16 | 17 | def __getattr__(self, attr): 18 | try: 19 | val = self._module.__dict__[attr] 20 | except KeyError, e: 21 | if _fallback.has_key(attr): 22 | val = _fallback[attr] 23 | else: 24 | raise e 25 | return val 26 | 27 | by_version = { 28 | '1.5': dis('1.5', 'dis_15'), 29 | '1.6': dis('1.6', 'dis_16'), 30 | '2.0': dis('2.0', 'dis_20'), 31 | '2.1': dis('2.1', 'dis_21'), 32 | '2.2': dis('2.2', 'dis_22'), 33 | '2.3': dis('2.3', 'dis_23'), 34 | '2.4': dis('2.4', 'dis_24'), 35 | '2.5': dis('2.5', 'dis_25'), 36 | } 37 | 38 | by_magic = dict( [ (mag, by_version[ver]) 39 | for mag, ver in magics.versions.iteritems() ] ) 40 | 41 | if __name__ == '__main__': 42 | for m, ver in by_magic.items(): 43 | magics.__show(ver, m) 44 | print by_version['2.2'].hasjrel 45 | -------------------------------------------------------------------------------- /decompyle/decompyle/magics.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | __all__ = ['magics', 'versions'] 4 | 5 | def __build_magic(magic): 6 | return struct.pack('Hcc', magic, '\r', '\n') 7 | 8 | def __by_version(magics): 9 | by_version = {} 10 | for m, v in magics.items(): 11 | by_version[v] = m 12 | return by_version 13 | 14 | versions = { 15 | # magic, version 16 | __build_magic(20121): '1.5', 17 | __build_magic(50428): '1.6', 18 | __build_magic(50823): '2.0', 19 | __build_magic(60202): '2.1', 20 | __build_magic(60717): '2.2', 21 | __build_magic(62011): '2.3', 22 | __build_magic(62061): '2.4', 23 | __build_magic(62131): '2.5', 24 | } 25 | 26 | magics = __by_version(versions) 27 | 28 | def __show(text, magic): 29 | print text, struct.unpack('BBBB', magic), \ 30 | struct.unpack('HBB', magic) 31 | 32 | def test(): 33 | import imp 34 | magic_20 = by_version['2.0'] 35 | current = imp.get_magic() 36 | current_version = magics[current] 37 | magic_current = by_version[ current_version ] 38 | print type(magic_20), len(magic_20), repr(magic_20) 39 | print 40 | print 'This Python interpreter has version', current_version 41 | __show('imp.get_magic():\t', current), 42 | __show('magic[current_version]:\t', magic_current) 43 | __show('magic_20:\t\t', magic_20) 44 | 45 | if __name__ == '__main__': 46 | test() 47 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 2 | 3 | $ UNHOLY $ 4 | 5 | Compile Ruby to Python bytecode. 6 | And, in addition, translate that 7 | bytecode back to Python source 8 | code using Decompyle (included.) 9 | 10 | Requires Ruby 1.9 and Python 2.5. 11 | 12 | 13 | $ INSTALL $ 14 | 15 | First, install decompyle: 16 | 17 | > cd decompyle 18 | > python setup.py build 19 | # python setup.py install 20 | 21 | Then, in the main directory, use 22 | the tools. 23 | 24 | --- 25 | 26 | To compile Ruby to a .pyc: 27 | 28 | > bin/unholy test.rb 29 | > PYTHONPATH=python \ 30 | python test.rb.pyc 31 | 32 | --- 33 | 34 | To translate to Python: 35 | 36 | > decompyle test.rb.pyc > test.py 37 | 38 | --- 39 | 40 | And, to view the disassembled 41 | bytes: 42 | 43 | > bin/py-dis test.rb.pyc 44 | 45 | Thanks to Ned Batchelder for his 46 | rather juicy posts on dissecting 47 | Python bytecode. It is only too 48 | bad that a Rubyist got a hold of 49 | them. :( 50 | 51 | $ POTION $ 52 | 53 | Now, image if Ruby and Python 54 | were to combine into something 55 | new. Let's call it "potion": 56 | 57 | > potion test.py 58 | HELLO FROM PYTHON 59 | > potion test.rb 60 | KONNICHIWA FROM RUBY 61 | 62 | You know, it's crazy that Python 63 | and Ruby fans find themselves 64 | battling so much. While syntax 65 | is different, this exercise 66 | proves how close they are to 67 | each other! And, yes, I like 68 | Ruby's syntax and can think much 69 | better in it, but it would be 70 | nice to share libs with Python 71 | folk and not have to wait forever 72 | for a mythical VM that runs all 73 | possible languages. 74 | 75 | -------------------------------------------------------------------------------- /decompyle/test/test_nested_elif.py: -------------------------------------------------------------------------------- 1 | # nested_elif.py -- source test pattern for nested elif 2 | # 3 | # This simple program is part of the decompyle test suite. 4 | # 5 | # decompyle is a Python byte-code decompiler 6 | # See http://www.goebel-consult.de/decompyle/ for download and 7 | # for further information 8 | 9 | a = None 10 | 11 | if a == 1: 12 | print '1' 13 | elif a == 2: 14 | print '2' 15 | 16 | if a == 1: 17 | print '1' 18 | elif a == 2: 19 | print '2' 20 | else: 21 | print 'other' 22 | 23 | if a == 1: 24 | print '1' 25 | elif a == 2: 26 | print '2' 27 | elif a == 3: 28 | print '3' 29 | else: 30 | print 'other' 31 | 32 | if a == 1: 33 | print '1' 34 | elif a == 2: 35 | print '2' 36 | elif a == 3: 37 | print '3' 38 | 39 | if a == 1: 40 | print '1' 41 | else: 42 | if a == 2: 43 | print '2' 44 | else: 45 | if a == 3: 46 | print '3' 47 | else: 48 | print 'other' 49 | 50 | if a == 1: 51 | print '1' 52 | else: 53 | if a == 2: 54 | print '2' 55 | else: 56 | print 'more' 57 | if a == 3: 58 | print '3' 59 | else: 60 | print 'other' 61 | 62 | if a == 1: 63 | print '1' 64 | else: 65 | print 'more' 66 | if a == 2: 67 | print '2' 68 | else: 69 | if a == 3: 70 | print '3' 71 | else: 72 | print 'other' 73 | 74 | if a == 1: 75 | print '1' 76 | else: 77 | print 'more' 78 | if a == 2: 79 | print '2' 80 | else: 81 | print 'more' 82 | if a == 3: 83 | print '3' 84 | elif a == 4: 85 | print '4' 86 | elif a == 4: 87 | print '4' 88 | else: 89 | print 'other' 90 | -------------------------------------------------------------------------------- /bin/py-dis: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import dis, marshal, struct, sys, time, types 3 | 4 | def show_file(fname): 5 | f = open(fname, "rb") 6 | magic = f.read(4) 7 | moddate = f.read(4) 8 | modtime = time.asctime(time.localtime(struct.unpack('L', moddate)[0])) 9 | print "magic %s" % (magic.encode('hex')) 10 | print "moddate %s (%s)" % (moddate.encode('hex'), modtime) 11 | code = marshal.load(f) 12 | show_code(code) 13 | 14 | def show_code(code, indent=''): 15 | print "%scode" % indent 16 | indent += ' ' 17 | print "%sargcount %d" % (indent, code.co_argcount) 18 | print "%snlocals %d" % (indent, code.co_nlocals) 19 | print "%sstacksize %d" % (indent, code.co_stacksize) 20 | print "%sflags %04x" % (indent, code.co_flags) 21 | show_hex("code", code.co_code, indent=indent) 22 | dis.disassemble(code) 23 | print "%sconsts" % indent 24 | for const in code.co_consts: 25 | if type(const) == types.CodeType: 26 | show_code(const, indent+' ') 27 | else: 28 | print " %s%r" % (indent, const) 29 | print "%snames %r" % (indent, code.co_names) 30 | print "%svarnames %r" % (indent, code.co_varnames) 31 | print "%sfreevars %r" % (indent, code.co_freevars) 32 | print "%scellvars %r" % (indent, code.co_cellvars) 33 | print "%sfilename %r" % (indent, code.co_filename) 34 | print "%sname %r" % (indent, code.co_name) 35 | print "%sfirstlineno %d" % (indent, code.co_firstlineno) 36 | show_hex("lnotab", code.co_lnotab, indent=indent) 37 | 38 | def show_hex(label, h, indent): 39 | h = h.encode('hex') 40 | if len(h) < 60: 41 | print "%s%s %s" % (indent, label, h) 42 | else: 43 | print "%s%s" % (indent, label) 44 | for i in range(0, len(h), 60): 45 | print "%s %s" % (indent, h[i:i+60]) 46 | 47 | show_file(sys.argv[1]) 48 | -------------------------------------------------------------------------------- /decompyle/test/test_nested_scopes.py: -------------------------------------------------------------------------------- 1 | # test_nested_scopes.py -- source test pattern for nested scopes 2 | # 3 | # This source is part of the decompyle test suite. 4 | # 5 | # decompyle is a Python byte-code decompiler 6 | # See http://www.goebel-consult.de/decompyle/ for download and 7 | # for further information 8 | 9 | from __future__ import nested_scopes 10 | 11 | blurb = 1 12 | 13 | def k0(): 14 | def l0(m=1): 15 | print 16 | l0() 17 | 18 | def x0(): 19 | def y0(): 20 | print 21 | y0() 22 | 23 | def x1(): 24 | def y1(): 25 | print 'y-blurb =', blurb 26 | y1() 27 | 28 | def x2(): 29 | def y2(): 30 | print 31 | blurb = 2 32 | y2() 33 | 34 | def x3a(): 35 | def y3a(x): 36 | print 'y-blurb =', blurb, flurb 37 | print 38 | blurb = 3 39 | flurb = 7 40 | y3a(1) 41 | print 'x3a-blurb =', blurb 42 | 43 | def x3(): 44 | def y3(x): 45 | def z(): 46 | blurb = 25 47 | print 'z-blurb =', blurb, 48 | z() 49 | print 'y-blurb =', blurb, 50 | print 51 | blurb = 3 52 | y3(1) 53 | print 'x3-blurb =', blurb 54 | 55 | def x3b(): 56 | def y3b(x): 57 | def z(): 58 | print 'z-blurb =', blurb, 59 | blurb = 25 60 | z() 61 | print 'y-blurb =', blurb, 62 | print 63 | blurb = 3 64 | y3b(1) 65 | print 'x3-blurb =', blurb 66 | 67 | def x4(): 68 | def y4(x): 69 | def z(): 70 | print 'z-blurb =', blurb 71 | z() 72 | global blurb 73 | blurb = 3 74 | y4(1) 75 | 76 | def x(): 77 | def y(x): 78 | print 'y-blurb =', blurb 79 | blurb = 2 80 | y(1) 81 | 82 | 83 | def func_with_tuple_args6((a,b), (c,d)=(2,3), *args, **kwargs): 84 | def y(x): 85 | print 'y-a =', a 86 | print c 87 | 88 | def find(self, name): 89 | # This is taken from 'What's new in Python 2.1' by amk 90 | L = filter(lambda x, name: x == name, self.list_attribute) 91 | 92 | x0(); x1(); x2(); 93 | x3(); x3a(); x3b(); 94 | x4(); x() 95 | print 'blurb =', blurb 96 | -------------------------------------------------------------------------------- /decompyle/test/test_exceptions.py: -------------------------------------------------------------------------------- 1 | import dis 2 | 3 | def x11(): 4 | try: 5 | a = 'try except' 6 | except: 7 | a = 2 8 | b = '--------' 9 | 10 | 11 | def x12(): 12 | try: 13 | a = 'try except else(pass)' 14 | except: 15 | a = 2 16 | b = '--------' 17 | 18 | 19 | def x13(): 20 | try: 21 | a = 'try except else(a=3)' 22 | except: 23 | a = 2 24 | else: 25 | a = 3 26 | b = '--------' 27 | 28 | 29 | def x21(): 30 | try: 31 | a = 'try KeyError' 32 | except KeyError: 33 | a = 8 34 | b = '--------' 35 | 36 | 37 | def x22(): 38 | try: 39 | a = 'try (IdxErr, KeyError) else(pass)' 40 | except (IndexError, KeyError): 41 | a = 8 42 | b = '--------' 43 | 44 | 45 | def x23(): 46 | try: 47 | a = 'try KeyError else(a=9)' 48 | except KeyError: 49 | a = 8 50 | else: 51 | a = 9 52 | b = '--------' 53 | 54 | 55 | def x31(): 56 | try: 57 | a = 'try KeyError IndexError' 58 | except KeyError: 59 | a = 8 60 | except IndexError: 61 | a = 9 62 | b = '--------' 63 | 64 | 65 | def x32(): 66 | try: 67 | a = 'try KeyError IndexError else(pass)' 68 | except KeyError: 69 | a = 8 70 | except IndexError: 71 | a = 9 72 | b = '--------' 73 | 74 | 75 | def x33(): 76 | try: 77 | a = 'try KeyError IndexError else(a=9)' 78 | except KeyError: 79 | a = 8 80 | except IndexError: 81 | a = 9 82 | else: 83 | a = 9 84 | b = '#################' 85 | 86 | 87 | def x41(): 88 | if (a == 1): 89 | a = 1 90 | elif (b == 1): 91 | b = 1 92 | else: 93 | c = 1 94 | b = '#################' 95 | 96 | 97 | def x42(): 98 | if (a == 1): 99 | a = 1 100 | elif (b == 1): 101 | b = 1 102 | else: 103 | c = 1 104 | xxx = 'mmm' 105 | 106 | if (__name__ == '__main__'): 107 | dis.dis(xx) 108 | -------------------------------------------------------------------------------- /decompyle/test/compile_tests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | compile_tests -- compile test patterns for the decompyle test suite 4 | 5 | This source is part of the decompyle test suite. 6 | 7 | decompyle is a Python byte-code decompiler 8 | See http://www.goebel-consult.de/decompyle/ for download and 9 | for further information 10 | """ 11 | 12 | import py_compile, os, sys, getopt 13 | 14 | work_dir = os.path.dirname(sys.argv[0]) 15 | src_dir = work_dir 16 | 17 | opts, args = getopt.getopt(sys.argv[1:], 's:w:') 18 | 19 | for opt, val in opts: 20 | if opt == '-s': 21 | src_dir = val 22 | if opt == '-w': 23 | work_dir = val 24 | else: 25 | raise "Unknown Option '%s'" % opt 26 | if args: 27 | raise 'This tool does not want any arguments' 28 | 29 | print "Using files in dir %s" % src_dir 30 | print "Compiling into dir %s" % work_dir 31 | 32 | tests = {} 33 | 34 | tests['1.5'] = ["class", "del", "docstring", 'empty', "exec", 35 | "exceptions", "expressions", "functions", "global", 36 | "globals", "import", "integers", "lambda", "loops", 37 | "misc", "nested_elif", "prettyprint", "print", 38 | 'single_stmt', "slices", "tuple_params", 'tuples'] 39 | 40 | tests['1.6'] = ["applyEquiv", ] + tests['1.5'] 41 | 42 | tests['2.0'] = ["augmentedAssign", "extendedImport", "extendedPrint", 43 | "import_as", "listComprehensions", 'print_to'] + \ 44 | tests['1.6'] # [ "--extendedarg", ] 45 | 46 | tests['2.1'] = ['loops2', 'nested_scopes'] + tests['2.0'] 47 | 48 | tests['2.2'] = ['divide_future', 'divide_no_future', 'iterators', 49 | 'yield'] + tests['2.1'] 50 | 51 | total_tests = len(tests['2.2']) 52 | #tests['2.2'].sort(); print tests['2.2'] 53 | 54 | extension = '.py' + (__debug__ and 'c' or 'o') 55 | 56 | def compile(file, target_dir): 57 | sfile = os.path.join(src_dir, 'test_%s.py' % file) 58 | cfile = os.path.join(target_dir, 'test_%s%s' % (file, extension) ) 59 | py_compile.compile(sfile, cfile=cfile) 60 | 61 | def compile_for_version(version): 62 | target_dir = os.path.join(work_dir, 'bytecode_' + version) 63 | if not os.path.exists(target_dir): 64 | os.mkdir(target_dir) 65 | for file in tests[version]: 66 | compile(file, target_dir) 67 | 68 | try: 69 | version = '%i.%i' % sys.version_info[:2] 70 | except AttributeError: 71 | version = sys.version[:3] 72 | 73 | print 'Compiling test files for Python', version, 74 | print '(%i/%i files)' % (len(tests[version]), total_tests) 75 | compile_for_version(version) 76 | print 'Done.' 77 | -------------------------------------------------------------------------------- /decompyle/scripts/decompyle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Mode: -*- python -*- 3 | # 4 | # Copyright (c) 2000-2002 by hartmut Goebel 5 | # 6 | """ 7 | Usage: decompyle [OPTIONS]... [ FILE | DIR]... 8 | 9 | Examples: 10 | decompyle foo.pyc bar.pyc # decompyle foo.pyc, bar.pyc to stdout 11 | decompyle -o . foo.pyc bar.pyc # decompyle to ./foo.dis and ./bar.dis 12 | decompyle -o /tmp /usr/lib/python1.5 # decompyle whole library 13 | 14 | Options: 15 | -o output decompyled files to this path: 16 | if multiple input files are decompyled, the common prefix 17 | is stripped from these names and the remainder appended to 18 | 19 | decompyle -o /tmp bla/fasel.pyc bla/foo.pyc 20 | -> /tmp/fasel.dis, /tmp/foo.dis 21 | decompyle -o /tmp bla/fasel.pyc bar/foo.pyc 22 | -> /tmp/bla/fasel.dis, /tmp/bar/foo.dis 23 | decompyle -o /tmp /usr/lib/python1.5 24 | -> /tmp/smtplib.dis ... /tmp/lib-tk/FixTk.dis 25 | --verify compare generated source with input byte-code 26 | (requires -o) 27 | --help show this message 28 | 29 | Debugging Options: 30 | --showasm include byte-code (disables --verify) 31 | --showast include AST (abstract syntax tree) (disables --verify) 32 | 33 | Extensions of generated files: 34 | '.dis' successfully decompyled (and verified if --verify) 35 | '.dis_unverified' successfully decompyled but --verify failed 36 | '.nodis' decompyle failed (contact author for enhancement) 37 | """ 38 | 39 | Usage_short = \ 40 | "decomyple [--help] [--verify] [--showasm] [--showast] [-o ] FILE|DIR..." 41 | 42 | import sys, os, getopt 43 | from decompyle import main, verify 44 | import time 45 | 46 | showasm = showast = do_verify = 0 47 | outfile = '-' 48 | out_base = None 49 | 50 | opts, files = getopt.getopt(sys.argv[1:], 'ho:', 51 | ['help', 'verify', 'showast', 'showasm']) 52 | for opt, val in opts: 53 | if opt in ('-h', '--help'): 54 | print __doc__ 55 | sys.exit(0) 56 | elif opt == '--verify': 57 | do_verify = 1 58 | elif opt == '--showasm': 59 | showasm = 1 60 | do_verify = 0 61 | elif opt == '--showast': 62 | showast = 1 63 | do_verify = 0 64 | elif opt == '-o': 65 | outfile = val 66 | else: 67 | print Usage_short 68 | sys.exit(1) 69 | 70 | # argl, commonprefix works on strings, not on path parts, 71 | # thus we must handle the case with files in 'some/classes' 72 | # and 'some/cmds' 73 | src_base = os.path.commonprefix(files) 74 | if src_base[-1:] != os.sep: 75 | src_base = os.path.dirname(src_base) 76 | if src_base: 77 | sb_len = len( os.path.join(src_base, '') ) 78 | files = map(lambda f: f[sb_len:], files) 79 | del sb_len 80 | 81 | if outfile == '-': 82 | outfile = None # use stdout 83 | elif outfile and os.path.isdir(outfile): 84 | out_base = outfile; outfile = None 85 | elif outfile and len(files) > 1: 86 | out_base = outfile; outfile = None 87 | 88 | try: 89 | main(src_base, out_base, files, outfile, showasm, showast, do_verify) 90 | except KeyboardInterrupt, OSError: 91 | pass 92 | except verify.VerifyCmpError: 93 | raise 94 | -------------------------------------------------------------------------------- /decompyle/test_pythonlib: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # emacs-mode: -*-python-*- 3 | """ 4 | test_pythonlib -- decompyle ans verify Python libraries 5 | 6 | Usage-Examples: 7 | 8 | test_pythonlib --all # decompyle all tests (suite + libs) 9 | test_pythonlib --all --verify # decomyple all tests and verify results 10 | test_pythonlib --test # decompyle only the testsuite 11 | test_pythonlib --2.2 --verify # decompyle and verify python lib 2.2 12 | 13 | Adding own test-trees: 14 | 15 | Step 1) Edit this file and add a new entry to 'test_options', eg. 16 | test_options['mylib'] = ('/usr/lib/mylib', PYOC, 'mylib') 17 | Step 2: Run the test: 18 | test_pythonlib --mylib # decompyle 'mylib' 19 | test_pythonlib --mylib --verify # decomyple verify 'mylib' 20 | """ 21 | 22 | import decompyle 23 | import os, time, shutil 24 | from fnmatch import fnmatch 25 | 26 | #----- configure this for your needs 27 | 28 | target_base = '/tmp/py-dis/' 29 | lib_prefix = '/usr/lib' 30 | 31 | PYC = ('*.pyc', ) 32 | PYO = ('*.pyo', ) 33 | PYOC = ('*.pyc', '*.pyo') 34 | 35 | test_options = { 36 | # name: (src_basedir, pattern, output_base_suffix) 37 | 'test': ('./test', PYOC, 'test'), 38 | '1.5': (os.path.join(lib_prefix, 'python1.5'), PYC, 'python-lib1.5'), 39 | '1.6': (os.path.join(lib_prefix, 'python1.6'), PYC, 'python-lib1.6'), 40 | '2.0': (os.path.join(lib_prefix, 'python2.0'), PYC, 'python-lib2.0'), 41 | '2.1': (os.path.join(lib_prefix, 'python2.1'), PYC, 'python-lib2.1'), 42 | '2.2': (os.path.join(lib_prefix, 'python2.2'), PYC, 'python-lib2.2'), 43 | } 44 | 45 | #----- 46 | 47 | def do_tests(src_dir, patterns, target_dir, start_with=None, do_verify=0): 48 | 49 | def visitor(files, dirname, names): 50 | files.extend( 51 | [os.path.normpath(os.path.join(dirname, n)) 52 | for n in names 53 | for pat in patterns 54 | if fnmatch(n, pat)]) 55 | 56 | files = [] 57 | cwd = os.getcwd() 58 | os.chdir(src_dir) 59 | os.path.walk(os.curdir, visitor, files) 60 | os.chdir(cwd) 61 | files.sort() 62 | 63 | if start_with: 64 | try: 65 | start_with = files.index(start_with) 66 | files = files[start_with:] 67 | print '>>> starting with file', files[0] 68 | except ValueError: 69 | pass 70 | 71 | print time.ctime() 72 | decompyle.main(src_dir, target_dir, files, do_verify=do_verify) 73 | print time.ctime() 74 | 75 | if __name__ == '__main__': 76 | import getopt, sys 77 | 78 | do_verify = 0 79 | test_dirs = [] 80 | start_with = None 81 | 82 | test_options_keys = test_options.keys(); test_options_keys.sort() 83 | opts, args = getopt.getopt(sys.argv[1:], '', 84 | ['start-with=', 'verify', 'all', ] \ 85 | + test_options_keys ) 86 | for opt, val in opts: 87 | if opt == '--verify': 88 | do_verify = 1 89 | elif opt == '--start-with': 90 | start_with = val 91 | elif opt[2:] in test_options_keys: 92 | test_dirs.append(test_options[opt[2:]]) 93 | elif opt == '--all': 94 | for val in test_options_keys: 95 | test_dirs.append(test_options[val]) 96 | 97 | for src_dir, pattern, target_dir in test_dirs: 98 | if os.path.exists(src_dir): 99 | target_dir = os.path.join(target_base, target_dir) 100 | if os.path.exists(target_dir): 101 | shutil.rmtree(target_dir, ignore_errors=1) 102 | do_tests(src_dir, pattern, target_dir, start_with, do_verify) 103 | else: 104 | print '### skipping', src_dir 105 | 106 | # python 1.5: 107 | 108 | # test/re_tests memory error 109 | # test/test_b1 memory error 110 | 111 | # Verification notes: 112 | # - xdrlib fails verification due the same lambda used twice 113 | # (verification is successfull when using original .pyo as 114 | # input) 115 | # 116 | -------------------------------------------------------------------------------- /decompyle/CHANGES: -------------------------------------------------------------------------------- 1 | release 2.3.2 2 | - tidied up copyright and changelog information for releases 2.3 and later 3 | 4 | release 2.3.1 (Dan Pascu) 5 | - implemented a structure detection technique that fixes problems with 6 | optimised jumps in Python >= 2.3. In the previous release (decompyle 2.3), 7 | these problems meant that some files were incorrectly decompiled and others 8 | could not be decompiled at all. With this new structure detection 9 | technique, thorough testing over the standard python libraries suggests 10 | that decompyle 2.3.1 can handle everything that decompyle 2.2beta1 could, 11 | plus new Python 2.3 bytecodes and constructs. 12 | 13 | release 2.3 (Dan Pascu) 14 | - support for Python 2.3 added 15 | - use the marshal and disassembly code from their respective python 16 | versions, so that decompyle can manipulate bytecode independently 17 | of the interpreter that runs decompyle itself (for example it can 18 | decompile python2.3 bytecode even when running under python2.2) 19 | 20 | release 2.2beta1 (hartmut Goebel) 21 | - support for Python 1.5 up to Python 2.2 22 | - no longer requires to be run with the Python interpreter version 23 | which generated the byte-code. 24 | - requires Python 2.2 25 | - pretty-prints docstrings, hashes, lists and tuples 26 | - decompyle is now a script and a package 27 | - added emacs mode-hint and tab-width for each file output 28 | - enhanced test suite: more test patterns, .pyc/.pyo included 29 | - avoids unnecessary 'global' statements 30 | - still untested: EXTENDED_ARG 31 | 32 | internal changes: 33 | - major code overhoul: splitted into several modules, clean-ups 34 | - use a list of valid magics instead of the single one from imp.py 35 | - uses copies of 'dis.py' for every supported version. This ensures 36 | correct disassemling of the byte-code. 37 | - use a single Walker and a single Parser, thus saving time and memory 38 | - use augmented assign and 'print >>' internally 39 | - optimized 'Walker.engine', the main part of code generation 40 | 41 | release 0.6.0: (hartmut Goebel) 42 | - extended print (Python 2.0) 43 | - extended import (Python 2.0) (may not cover all cases) 44 | - augmented assign (Python 2.0) (may not cover all cases) 45 | - list comprehensions (Python 2.0) 46 | - equivalent for 'apply' (Python 1.6) 47 | - if .. elif .. else are now nested as expected 48 | - assert test, data 49 | - unpack list corrected (was the same as unpack tuple) 50 | - fixed unpack tuple (trailing semicolon was missing) 51 | - major speed up :-) 52 | - reduced memory usage (pre-alpha-0.5 has increased it a lot) 53 | - still missing: EXTENDED_ARG 54 | 55 | pre-alpha-0.5: (hartmut Goebel) 56 | - *args, **kwargs 57 | - global 58 | - formal tuple parameters (eg. def a(self, (x,y,z)) ) 59 | - actual lambda parameters (eg. X(lambda z: z**2) ) 60 | - remove last 'return None' in procedures 61 | - remove last 'return locals()' in class definitions 62 | - docstrings 63 | 64 | pre-alpha-0.4: (hartmut Goebel) 65 | - assert 66 | - try/except/finally 67 | - parentheses in expressions 68 | - nested expressions 69 | - extracted dissassemble() from module dis and 70 | removed ugly redirect of stdout, thus saved a lot of 71 | ugly code and a lot of memory 72 | 73 | pre-alpha-0.3: (hartmut Goebel) 74 | - keyword arguments 75 | - some boolean expressions 76 | - and/or 77 | - complex conditions in if/while 78 | - read byte-code from .pyc without importing 79 | - access to the body of classes and modules 80 | - class and function definitions 81 | - a = b = c = xxx 82 | 83 | pre-alpha-0.1 -> pre-alpha-0.2: 84 | - SET_LINENO filtered out in lexer now 85 | - added support for subscripts (just for Christian Tismer :-) 86 | - fixed bug with handling of BUILD_{LIST,TUPLE} & CALL_FUNCTION 87 | - dict-building support 88 | - comparison support 89 | - exec support 90 | - del support 91 | - pass support 92 | - slice support 93 | - no more extraneous (albeit legal) commas 94 | - finally, it excepts try [sic] but not all 42 variations of it 95 | -------------------------------------------------------------------------------- /decompyle/README: -------------------------------------------------------------------------------- 1 | 2 | decompyle -- A Python byte-code decompiler 3 | 2.3 4 | 2004-12-12 5 | 6 | Introduction 7 | ------------ 8 | 9 | 'decompyle' converts Python byte-code back into equivalent Python 10 | source. It accepts byte-code from any Python version starting with 1.5 11 | up to 2.3. 12 | 13 | The generated source is very readable: docstrings, lists, tuples and 14 | hashes get pretty-printed. 15 | 16 | 'decompyle' may also verify the equivalence of the generated source by 17 | by compiling it and comparing both byte-codes. 18 | 19 | 'decompyle' is based on John Aycock's generic small languages compiler 20 | 'spark' (http://www.csr.uvic.ca/~aycock/python/) and his prior work on 21 | 'decompyle'. 22 | 23 | Additional note (3 July 2004, Ben Burton): 24 | 25 | The original website from which this software was obtained is no longer 26 | available. It has now become a commercial decompilation service, with 27 | no software available for download. 28 | 29 | Any developers seeking to make alterations or enhancements to this code 30 | should therefore consider these debian packages an appropriate starting 31 | point. 32 | 33 | Features 34 | -------- 35 | 36 | * decompyles Python byte-code into equivalent Python source 37 | 38 | * decompyles byte-code from Python versions 1.5 up to 2.3 39 | 40 | * pretty-prints docstrings, hashes, lists and tuples 41 | 42 | * supports an option for verifing the generated Python source is 43 | equivalent to the byte-code. This is done by compiling the 44 | generated source and comparing both byte-codes. 45 | 46 | * decompyles and successfully verifies 100% of the Python 1.5, 2.0, 47 | 2.1 and 2.2 library 48 | 49 | * decompyles and successfully verifies 100% of the Python 1.5 50 | library, including lib-stdwin, lib-tk, gnome and gtk. 51 | 52 | * reads directly from .pyc/.pyo files, bulk-decompyle whole 53 | directories 54 | 55 | * output may be written to file, a directory or to stdout 56 | 57 | * option for including byte-code disassembly into generated source 58 | 59 | * If translation fails, the part causing the problem is output. This 60 | helps improofing 'decompyle'. If this happens to you, please 61 | contact the author. 62 | 63 | For a list of changes please refer to the 'CHANGES' file. 64 | 65 | 66 | Requirements 67 | ------------ 68 | 69 | 'decompyle' requires Python 2.2 or later. 70 | 71 | 72 | Installation 73 | ------------ 74 | 75 | You may either create a RPM and install this, or install directly from 76 | the source distribution. 77 | 78 | Creating RPMS: 79 | 80 | python setup.py bdist_rpm 81 | 82 | If you need to force the python interpreter to eg. pyton2: 83 | python2 setup.py bdist_rpm --python=python2 84 | 85 | 86 | Installation from the source distribution: 87 | 88 | python setup.py install 89 | 90 | To install to a user's home-dir: 91 | python setup.py install --home= 92 | 93 | To install to another prefix (eg. /usr/local) 94 | python setup.py install --prefix=/usr/local 95 | 96 | If you need to force the python interpreter to eg. pyton2: 97 | python2 setup.py install 98 | 99 | For more information on 'Installing Python Modules' please refer to 100 | http://www.python.org/doc/current/inst/inst.html 101 | 102 | 103 | Usage 104 | ----- 105 | 106 | decompyle -h prints short usage 107 | decompyle --help prints long usage 108 | 109 | 110 | Known Bugs/Restrictions 111 | ----------------------- 112 | 113 | * EXTENDED_ARG token is untested (this is a new token for Python 2.0 114 | which is used only if many items exist within a code object). 115 | 116 | * Verifying decompyled source with optizimzed byte code (.pyo) when 117 | running without optimizations (option '-O' not given) fails in most 118 | cases. Same is true for vis-a-versa. This is due to the fact that 119 | Python generated different bytecode depending on option '-O'. 120 | 121 | 122 | * Python 2.2 generated different byte-code than prior version for the 123 | same source. This is due the intruduction of iterators. Currently 124 | 'decompyle' fails verifying this source if the byte-code was 125 | generated by an older version of Python. 126 | -------------------------------------------------------------------------------- /decompyle/test/test_prettyprint.py: -------------------------------------------------------------------------------- 1 | """ 2 | test_prettyprint.py -- source test pattern for tesing the prettyprint 3 | funcionality of decompyle 4 | 5 | This source is part of the decompyle test suite. 6 | 7 | decompyle is a Python byte-code decompiler 8 | See http://www.goebel-consult.de/decompyle/ for download and 9 | for further information 10 | """ 11 | 12 | import pprint 13 | 14 | aa = 'aa' 15 | 16 | dict0 = { 17 | 'a': 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 18 | 'b': 1234, 19 | 'd': aa, 20 | aa: aa 21 | } 22 | 23 | 24 | dict = { 25 | 'a': 'aaa', 26 | 'b': 1234, 27 | 'c': { 'ca': 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 28 | 'cb': 1234, 29 | 'cc': None 30 | }, 31 | 'd': aa, 32 | aa: aa, 33 | 'eee': { 'ca': 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 34 | 'cb': 1234, 35 | 'cc': None 36 | }, 37 | 'ff': aa, 38 | } 39 | list1 = [ '1aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 40 | aa, 41 | '1bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', 42 | '1ccccccccccccccccccccccccccccccccccccccccccc' ] 43 | list2 = [ '2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 44 | [ '22aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 45 | aa, 46 | '22bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', 47 | '22ccccccccccccccccccccccccccccccccccccccccccc' ], 48 | 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', 49 | 'ccccccccccccccccccccccccccccccccccccccccccc' ] 50 | tuple1 = ( '1aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 51 | aa, 52 | '1bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', 53 | '1ccccccccccccccccccccccccccccccccccccccccccc' ) 54 | tuple2 = ( '2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 55 | ( '22aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 56 | aa, 57 | '22bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', 58 | '22ccccccccccccccccccccccccccccccccccccccccccc' ), 59 | 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', 60 | 'ccccccccccccccccccccccccccccccccccccccccccc' ) 61 | 62 | def funcA(): 63 | dict = { 64 | 'a': 'aaa', 65 | 'b': 1234, 66 | 'c': { 'ca': 'aaa', 67 | 'cb': 1234, 68 | 'cc': None 69 | }, 70 | 'd': aa, 71 | aa: aa 72 | } 73 | list1 = [ '1aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 74 | '1bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', 75 | aa, 76 | '1ccccccccccccccccccccccccccccccccccccccccccc' ] 77 | list2 = [ '2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 78 | [ '22aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 79 | aa, 80 | '22bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', 81 | '22ccccccccccccccccccccccccccccccccccccccccccc' ], 82 | 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', 83 | 'ccccccccccccccccccccccccccccccccccccccccccc' ] 84 | tuple1 = ( '1aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 85 | '1bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', 86 | aa, 87 | '1ccccccccccccccccccccccccccccccccccccccccccc' ) 88 | tuple2 = ( '2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 89 | ( '22aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 90 | aa, 91 | '22bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', 92 | '22ccccccccccccccccccccccccccccccccccccccccccc' ), 93 | 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', 94 | 'ccccccccccccccccccccccccccccccccccccccccccc' ) 95 | 96 | def funcAB(): 97 | dict = { 98 | 'a': 'aaa', 99 | 'b': 1234, 100 | 'c': { 'ca': 'aaa', 101 | 'cb': 1234, 102 | 'cc': None 103 | }, 104 | 'd': aa, 105 | aa: aa 106 | } 107 | list1 = [ '1aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 108 | '1bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', 109 | '1ccccccccccccccccccccccccccccccccccccccccccc' ] 110 | list2 = [ '2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 111 | [ '22aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 112 | '22bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', 113 | '22ccccccccccccccccccccccccccccccccccccccccccc' ], 114 | 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', 115 | 'ccccccccccccccccccccccccccccccccccccccccccc' ] 116 | tuple1 = ( '1aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 117 | '1bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', 118 | '1ccccccccccccccccccccccccccccccccccccccccccc' ) 119 | tuple2 = ( '2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 120 | ( '22aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 121 | '22bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', 122 | '22ccccccccccccccccccccccccccccccccccccccccccc' ), 123 | 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', 124 | 'ccccccccccccccccccccccccccccccccccccccccccc' ) 125 | 126 | pprint.pprint(dict0) 127 | print 128 | pprint.pprint(dict) 129 | print 130 | 131 | pprint = pprint.PrettyPrinter(indent=2) 132 | pprint.pprint(dict0) 133 | print 134 | pprint.pprint(dict) 135 | print 136 | 137 | pprint.pprint(list1) 138 | print 139 | pprint.pprint(list2) 140 | -------------------------------------------------------------------------------- /decompyle/decompyle/opcode_23.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | opcode module - potentially shared between dis and other modules which 4 | operate on bytecodes (e.g. peephole optimizers). 5 | """ 6 | 7 | __all__ = ["cmp_op", "hasconst", "hasname", "hasjrel", "hasjabs", 8 | "haslocal", "hascompare", "hasfree", "opname", "opmap", 9 | "HAVE_ARGUMENT", "EXTENDED_ARG"] 10 | 11 | cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is', 12 | 'is not', 'exception match', 'BAD') 13 | 14 | hasconst = [] 15 | hasname = [] 16 | hasjrel = [] 17 | hasjabs = [] 18 | haslocal = [] 19 | hascompare = [] 20 | hasfree = [] 21 | 22 | opmap = {} 23 | opname = [''] * 256 24 | for op in range(256): opname[op] = '<' + `op` + '>' 25 | del op 26 | 27 | def def_op(name, op): 28 | opname[op] = name 29 | opmap[name] = op 30 | 31 | def name_op(name, op): 32 | def_op(name, op) 33 | hasname.append(op) 34 | 35 | def jrel_op(name, op): 36 | def_op(name, op) 37 | hasjrel.append(op) 38 | 39 | def jabs_op(name, op): 40 | def_op(name, op) 41 | hasjabs.append(op) 42 | 43 | # Instruction opcodes for compiled code 44 | 45 | def_op('STOP_CODE', 0) 46 | def_op('POP_TOP', 1) 47 | def_op('ROT_TWO', 2) 48 | def_op('ROT_THREE', 3) 49 | def_op('DUP_TOP', 4) 50 | def_op('ROT_FOUR', 5) 51 | 52 | def_op('UNARY_POSITIVE', 10) 53 | def_op('UNARY_NEGATIVE', 11) 54 | def_op('UNARY_NOT', 12) 55 | def_op('UNARY_CONVERT', 13) 56 | 57 | def_op('UNARY_INVERT', 15) 58 | 59 | def_op('BINARY_POWER', 19) 60 | 61 | def_op('BINARY_MULTIPLY', 20) 62 | def_op('BINARY_DIVIDE', 21) 63 | def_op('BINARY_MODULO', 22) 64 | def_op('BINARY_ADD', 23) 65 | def_op('BINARY_SUBTRACT', 24) 66 | def_op('BINARY_SUBSCR', 25) 67 | def_op('BINARY_FLOOR_DIVIDE', 26) 68 | def_op('BINARY_TRUE_DIVIDE', 27) 69 | def_op('INPLACE_FLOOR_DIVIDE', 28) 70 | def_op('INPLACE_TRUE_DIVIDE', 29) 71 | 72 | def_op('SLICE+0', 30) 73 | def_op('SLICE+1', 31) 74 | def_op('SLICE+2', 32) 75 | def_op('SLICE+3', 33) 76 | 77 | def_op('STORE_SLICE+0', 40) 78 | def_op('STORE_SLICE+1', 41) 79 | def_op('STORE_SLICE+2', 42) 80 | def_op('STORE_SLICE+3', 43) 81 | 82 | def_op('DELETE_SLICE+0', 50) 83 | def_op('DELETE_SLICE+1', 51) 84 | def_op('DELETE_SLICE+2', 52) 85 | def_op('DELETE_SLICE+3', 53) 86 | 87 | def_op('INPLACE_ADD', 55) 88 | def_op('INPLACE_SUBTRACT', 56) 89 | def_op('INPLACE_MULTIPLY', 57) 90 | def_op('INPLACE_DIVIDE', 58) 91 | def_op('INPLACE_MODULO', 59) 92 | def_op('STORE_SUBSCR', 60) 93 | def_op('DELETE_SUBSCR', 61) 94 | 95 | def_op('BINARY_LSHIFT', 62) 96 | def_op('BINARY_RSHIFT', 63) 97 | def_op('BINARY_AND', 64) 98 | def_op('BINARY_XOR', 65) 99 | def_op('BINARY_OR', 66) 100 | def_op('INPLACE_POWER', 67) 101 | def_op('GET_ITER', 68) 102 | 103 | def_op('PRINT_EXPR', 70) 104 | def_op('PRINT_ITEM', 71) 105 | def_op('PRINT_NEWLINE', 72) 106 | def_op('PRINT_ITEM_TO', 73) 107 | def_op('PRINT_NEWLINE_TO', 74) 108 | def_op('INPLACE_LSHIFT', 75) 109 | def_op('INPLACE_RSHIFT', 76) 110 | def_op('INPLACE_AND', 77) 111 | def_op('INPLACE_XOR', 78) 112 | def_op('INPLACE_OR', 79) 113 | def_op('BREAK_LOOP', 80) 114 | 115 | def_op('LOAD_LOCALS', 82) 116 | def_op('RETURN_VALUE', 83) 117 | def_op('IMPORT_STAR', 84) 118 | def_op('EXEC_STMT', 85) 119 | def_op('YIELD_VALUE', 86) 120 | 121 | def_op('POP_BLOCK', 87) 122 | def_op('END_FINALLY', 88) 123 | def_op('BUILD_CLASS', 89) 124 | 125 | HAVE_ARGUMENT = 90 # Opcodes from here have an argument: 126 | 127 | name_op('STORE_NAME', 90) # Index in name list 128 | name_op('DELETE_NAME', 91) # "" 129 | def_op('UNPACK_SEQUENCE', 92) # Number of tuple items 130 | jrel_op('FOR_ITER', 93) 131 | 132 | name_op('STORE_ATTR', 95) # Index in name list 133 | name_op('DELETE_ATTR', 96) # "" 134 | name_op('STORE_GLOBAL', 97) # "" 135 | name_op('DELETE_GLOBAL', 98) # "" 136 | def_op('DUP_TOPX', 99) # number of items to duplicate 137 | def_op('LOAD_CONST', 100) # Index in const list 138 | hasconst.append(100) 139 | name_op('LOAD_NAME', 101) # Index in name list 140 | def_op('BUILD_TUPLE', 102) # Number of tuple items 141 | def_op('BUILD_LIST', 103) # Number of list items 142 | def_op('BUILD_MAP', 104) # Always zero for now 143 | name_op('LOAD_ATTR', 105) # Index in name list 144 | def_op('COMPARE_OP', 106) # Comparison operator 145 | hascompare.append(106) 146 | name_op('IMPORT_NAME', 107) # Index in name list 147 | name_op('IMPORT_FROM', 108) # Index in name list 148 | 149 | jrel_op('JUMP_FORWARD', 110) # Number of bytes to skip 150 | jrel_op('JUMP_IF_FALSE', 111) # "" 151 | jrel_op('JUMP_IF_TRUE', 112) # "" 152 | jabs_op('JUMP_ABSOLUTE', 113) # Target byte offset from beginning of code 153 | 154 | name_op('LOAD_GLOBAL', 116) # Index in name list 155 | 156 | jabs_op('CONTINUE_LOOP', 119) # Target address 157 | jrel_op('SETUP_LOOP', 120) # Distance to target address 158 | jrel_op('SETUP_EXCEPT', 121) # "" 159 | jrel_op('SETUP_FINALLY', 122) # "" 160 | 161 | def_op('LOAD_FAST', 124) # Local variable number 162 | haslocal.append(124) 163 | def_op('STORE_FAST', 125) # Local variable number 164 | haslocal.append(125) 165 | def_op('DELETE_FAST', 126) # Local variable number 166 | haslocal.append(126) 167 | 168 | def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3) 169 | def_op('CALL_FUNCTION', 131) # #args + (#kwargs << 8) 170 | def_op('MAKE_FUNCTION', 132) # Number of args with default values 171 | def_op('BUILD_SLICE', 133) # Number of items 172 | 173 | def_op('MAKE_CLOSURE', 134) 174 | def_op('LOAD_CLOSURE', 135) 175 | hasfree.append(135) 176 | def_op('LOAD_DEREF', 136) 177 | hasfree.append(136) 178 | def_op('STORE_DEREF', 137) 179 | hasfree.append(137) 180 | 181 | def_op('CALL_FUNCTION_VAR', 140) # #args + (#kwargs << 8) 182 | def_op('CALL_FUNCTION_KW', 141) # #args + (#kwargs << 8) 183 | def_op('CALL_FUNCTION_VAR_KW', 142) # #args + (#kwargs << 8) 184 | 185 | def_op('EXTENDED_ARG', 143) 186 | EXTENDED_ARG = 143 187 | 188 | del def_op, name_op, jrel_op, jabs_op 189 | -------------------------------------------------------------------------------- /decompyle/decompyle/opcode_25.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | opcode module - potentially shared between dis and other modules which 4 | operate on bytecodes (e.g. peephole optimizers). 5 | """ 6 | 7 | __all__ = ["cmp_op", "hasconst", "hasname", "hasjrel", "hasjabs", 8 | "haslocal", "hascompare", "hasfree", "opname", "opmap", 9 | "HAVE_ARGUMENT", "EXTENDED_ARG"] 10 | 11 | cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is', 12 | 'is not', 'exception match', 'BAD') 13 | 14 | hasconst = [] 15 | hasname = [] 16 | hasjrel = [] 17 | hasjabs = [] 18 | haslocal = [] 19 | hascompare = [] 20 | hasfree = [] 21 | 22 | opmap = {} 23 | opname = [''] * 256 24 | for op in range(256): opname[op] = '<%r>' % (op,) 25 | del op 26 | 27 | def def_op(name, op): 28 | opname[op] = name 29 | opmap[name] = op 30 | 31 | def name_op(name, op): 32 | def_op(name, op) 33 | hasname.append(op) 34 | 35 | def jrel_op(name, op): 36 | def_op(name, op) 37 | hasjrel.append(op) 38 | 39 | def jabs_op(name, op): 40 | def_op(name, op) 41 | hasjabs.append(op) 42 | 43 | # Instruction opcodes for compiled code 44 | # Blank lines correspond to available opcodes 45 | 46 | def_op('STOP_CODE', 0) 47 | def_op('POP_TOP', 1) 48 | def_op('ROT_TWO', 2) 49 | def_op('ROT_THREE', 3) 50 | def_op('DUP_TOP', 4) 51 | def_op('ROT_FOUR', 5) 52 | 53 | def_op('NOP', 9) 54 | def_op('UNARY_POSITIVE', 10) 55 | def_op('UNARY_NEGATIVE', 11) 56 | def_op('UNARY_NOT', 12) 57 | def_op('UNARY_CONVERT', 13) 58 | 59 | def_op('UNARY_INVERT', 15) 60 | 61 | def_op('LIST_APPEND', 18) 62 | def_op('BINARY_POWER', 19) 63 | def_op('BINARY_MULTIPLY', 20) 64 | def_op('BINARY_DIVIDE', 21) 65 | def_op('BINARY_MODULO', 22) 66 | def_op('BINARY_ADD', 23) 67 | def_op('BINARY_SUBTRACT', 24) 68 | def_op('BINARY_SUBSCR', 25) 69 | def_op('BINARY_FLOOR_DIVIDE', 26) 70 | def_op('BINARY_TRUE_DIVIDE', 27) 71 | def_op('INPLACE_FLOOR_DIVIDE', 28) 72 | def_op('INPLACE_TRUE_DIVIDE', 29) 73 | def_op('SLICE+0', 30) 74 | def_op('SLICE+1', 31) 75 | def_op('SLICE+2', 32) 76 | def_op('SLICE+3', 33) 77 | 78 | def_op('STORE_SLICE+0', 40) 79 | def_op('STORE_SLICE+1', 41) 80 | def_op('STORE_SLICE+2', 42) 81 | def_op('STORE_SLICE+3', 43) 82 | 83 | def_op('DELETE_SLICE+0', 50) 84 | def_op('DELETE_SLICE+1', 51) 85 | def_op('DELETE_SLICE+2', 52) 86 | def_op('DELETE_SLICE+3', 53) 87 | 88 | def_op('INPLACE_ADD', 55) 89 | def_op('INPLACE_SUBTRACT', 56) 90 | def_op('INPLACE_MULTIPLY', 57) 91 | def_op('INPLACE_DIVIDE', 58) 92 | def_op('INPLACE_MODULO', 59) 93 | def_op('STORE_SUBSCR', 60) 94 | def_op('DELETE_SUBSCR', 61) 95 | def_op('BINARY_LSHIFT', 62) 96 | def_op('BINARY_RSHIFT', 63) 97 | def_op('BINARY_AND', 64) 98 | def_op('BINARY_XOR', 65) 99 | def_op('BINARY_OR', 66) 100 | def_op('INPLACE_POWER', 67) 101 | def_op('GET_ITER', 68) 102 | 103 | def_op('PRINT_EXPR', 70) 104 | def_op('PRINT_ITEM', 71) 105 | def_op('PRINT_NEWLINE', 72) 106 | def_op('PRINT_ITEM_TO', 73) 107 | def_op('PRINT_NEWLINE_TO', 74) 108 | def_op('INPLACE_LSHIFT', 75) 109 | def_op('INPLACE_RSHIFT', 76) 110 | def_op('INPLACE_AND', 77) 111 | def_op('INPLACE_XOR', 78) 112 | def_op('INPLACE_OR', 79) 113 | def_op('BREAK_LOOP', 80) 114 | def_op('WITH_CLEANUP', 81) 115 | def_op('LOAD_LOCALS', 82) 116 | def_op('RETURN_VALUE', 83) 117 | def_op('IMPORT_STAR', 84) 118 | def_op('EXEC_STMT', 85) 119 | def_op('YIELD_VALUE', 86) 120 | def_op('POP_BLOCK', 87) 121 | def_op('END_FINALLY', 88) 122 | def_op('BUILD_CLASS', 89) 123 | 124 | HAVE_ARGUMENT = 90 # Opcodes from here have an argument: 125 | 126 | name_op('STORE_NAME', 90) # Index in name list 127 | name_op('DELETE_NAME', 91) # "" 128 | def_op('UNPACK_SEQUENCE', 92) # Number of tuple items 129 | jrel_op('FOR_ITER', 93) 130 | 131 | name_op('STORE_ATTR', 95) # Index in name list 132 | name_op('DELETE_ATTR', 96) # "" 133 | name_op('STORE_GLOBAL', 97) # "" 134 | name_op('DELETE_GLOBAL', 98) # "" 135 | def_op('DUP_TOPX', 99) # number of items to duplicate 136 | def_op('LOAD_CONST', 100) # Index in const list 137 | hasconst.append(100) 138 | name_op('LOAD_NAME', 101) # Index in name list 139 | def_op('BUILD_TUPLE', 102) # Number of tuple items 140 | def_op('BUILD_LIST', 103) # Number of list items 141 | def_op('BUILD_MAP', 104) # Always zero for now 142 | name_op('LOAD_ATTR', 105) # Index in name list 143 | def_op('COMPARE_OP', 106) # Comparison operator 144 | hascompare.append(106) 145 | name_op('IMPORT_NAME', 107) # Index in name list 146 | name_op('IMPORT_FROM', 108) # Index in name list 147 | 148 | jrel_op('JUMP_FORWARD', 110) # Number of bytes to skip 149 | jrel_op('JUMP_IF_FALSE', 111) # "" 150 | jrel_op('JUMP_IF_TRUE', 112) # "" 151 | jabs_op('JUMP_ABSOLUTE', 113) # Target byte offset from beginning of code 152 | 153 | name_op('LOAD_GLOBAL', 116) # Index in name list 154 | 155 | jabs_op('CONTINUE_LOOP', 119) # Target address 156 | jrel_op('SETUP_LOOP', 120) # Distance to target address 157 | jrel_op('SETUP_EXCEPT', 121) # "" 158 | jrel_op('SETUP_FINALLY', 122) # "" 159 | 160 | def_op('LOAD_FAST', 124) # Local variable number 161 | haslocal.append(124) 162 | def_op('STORE_FAST', 125) # Local variable number 163 | haslocal.append(125) 164 | def_op('DELETE_FAST', 126) # Local variable number 165 | haslocal.append(126) 166 | 167 | def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3) 168 | def_op('CALL_FUNCTION', 131) # #args + (#kwargs << 8) 169 | def_op('MAKE_FUNCTION', 132) # Number of args with default values 170 | def_op('BUILD_SLICE', 133) # Number of items 171 | def_op('MAKE_CLOSURE', 134) 172 | def_op('LOAD_CLOSURE', 135) 173 | hasfree.append(135) 174 | def_op('LOAD_DEREF', 136) 175 | hasfree.append(136) 176 | def_op('STORE_DEREF', 137) 177 | hasfree.append(137) 178 | 179 | def_op('CALL_FUNCTION_VAR', 140) # #args + (#kwargs << 8) 180 | def_op('CALL_FUNCTION_KW', 141) # #args + (#kwargs << 8) 181 | def_op('CALL_FUNCTION_VAR_KW', 142) # #args + (#kwargs << 8) 182 | def_op('EXTENDED_ARG', 143) 183 | EXTENDED_ARG = 143 184 | 185 | del def_op, name_op, jrel_op, jabs_op 186 | -------------------------------------------------------------------------------- /decompyle/decompyle/dis_15.py: -------------------------------------------------------------------------------- 1 | """Disassembler of Python byte code into mnemonics.""" 2 | 3 | import sys 4 | import string 5 | import types 6 | 7 | def dis(x=None): 8 | """Disassemble classes, methods, functions, or code. 9 | 10 | With no argument, disassemble the last traceback. 11 | 12 | """ 13 | if not x: 14 | distb() 15 | return 16 | if type(x) is types.InstanceType: 17 | x = x.__class__ 18 | if hasattr(x, '__dict__'): 19 | items = x.__dict__.items() 20 | items.sort() 21 | for name, x1 in items: 22 | if type(x1) in (types.MethodType, 23 | types.FunctionType, 24 | types.CodeType): 25 | print "Disassembly of %s:" % name 26 | try: 27 | dis(x1) 28 | except TypeError, msg: 29 | print "Sorry:", msg 30 | print 31 | else: 32 | if hasattr(x, 'im_func'): 33 | x = x.im_func 34 | if hasattr(x, 'func_code'): 35 | x = x.func_code 36 | if hasattr(x, 'co_code'): 37 | disassemble(x) 38 | else: 39 | raise TypeError, \ 40 | "don't know how to disassemble %s objects" % \ 41 | type(x).__name__ 42 | 43 | def distb(tb=None): 44 | """Disassemble a traceback (default: last traceback).""" 45 | if not tb: 46 | try: 47 | tb = sys.last_traceback 48 | except AttributeError: 49 | raise RuntimeError, "no last traceback to disassemble" 50 | while tb.tb_next: tb = tb.tb_next 51 | disassemble(tb.tb_frame.f_code, tb.tb_lasti) 52 | 53 | def disassemble(co, lasti=-1): 54 | """Disassemble a code object.""" 55 | code = co.co_code 56 | labels = findlabels(code) 57 | n = len(code) 58 | i = 0 59 | while i < n: 60 | c = code[i] 61 | op = ord(c) 62 | if op == SET_LINENO and i > 0: print # Extra blank line 63 | if i == lasti: print '-->', 64 | else: print ' ', 65 | if i in labels: print '>>', 66 | else: print ' ', 67 | print string.rjust(`i`, 4), 68 | print string.ljust(opname[op], 15), 69 | i = i+1 70 | if op >= HAVE_ARGUMENT: 71 | oparg = ord(code[i]) + ord(code[i+1])*256 72 | i = i+2 73 | print string.rjust(`oparg`, 5), 74 | if op in hasconst: 75 | print '(' + `co.co_consts[oparg]` + ')', 76 | elif op in hasname: 77 | print '(' + co.co_names[oparg] + ')', 78 | elif op in hasjrel: 79 | print '(to ' + `i + oparg` + ')', 80 | elif op in haslocal: 81 | print '(' + co.co_varnames[oparg] + ')', 82 | elif op in hascompare: 83 | print '(' + cmp_op[oparg] + ')', 84 | print 85 | 86 | disco = disassemble 87 | 88 | def findlabels(code): 89 | """Detect all offsets in a byte code which are jump targets. 90 | 91 | Return the list of offsets. 92 | 93 | """ 94 | labels = [] 95 | n = len(code) 96 | i = 0 97 | while i < n: 98 | c = code[i] 99 | op = ord(c) 100 | i = i+1 101 | if op >= HAVE_ARGUMENT: 102 | oparg = ord(code[i]) + ord(code[i+1])*256 103 | i = i+2 104 | label = -1 105 | if op in hasjrel: 106 | label = i+oparg 107 | elif op in hasjabs: 108 | label = oparg 109 | if label >= 0: 110 | if label not in labels: 111 | labels.append(label) 112 | return labels 113 | 114 | cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is', 115 | 'is not', 'exception match', 'BAD') 116 | 117 | hasconst = [] 118 | hasname = [] 119 | hasjrel = [] 120 | hasjabs = [] 121 | haslocal = [] 122 | hascompare = [] 123 | 124 | opname = [''] * 256 125 | for op in range(256): opname[op] = '<' + `op` + '>' 126 | 127 | def def_op(name, op): 128 | opname[op] = name 129 | 130 | def name_op(name, op): 131 | opname[op] = name 132 | hasname.append(op) 133 | 134 | def jrel_op(name, op): 135 | opname[op] = name 136 | hasjrel.append(op) 137 | 138 | def jabs_op(name, op): 139 | opname[op] = name 140 | hasjabs.append(op) 141 | 142 | # Instruction opcodes for compiled code 143 | 144 | def_op('STOP_CODE', 0) 145 | def_op('POP_TOP', 1) 146 | def_op('ROT_TWO', 2) 147 | def_op('ROT_THREE', 3) 148 | def_op('DUP_TOP', 4) 149 | 150 | def_op('UNARY_POSITIVE', 10) 151 | def_op('UNARY_NEGATIVE', 11) 152 | def_op('UNARY_NOT', 12) 153 | def_op('UNARY_CONVERT', 13) 154 | 155 | def_op('UNARY_INVERT', 15) 156 | 157 | def_op('BINARY_POWER', 19) 158 | 159 | def_op('BINARY_MULTIPLY', 20) 160 | def_op('BINARY_DIVIDE', 21) 161 | def_op('BINARY_MODULO', 22) 162 | def_op('BINARY_ADD', 23) 163 | def_op('BINARY_SUBTRACT', 24) 164 | def_op('BINARY_SUBSCR', 25) 165 | 166 | def_op('SLICE+0', 30) 167 | def_op('SLICE+1', 31) 168 | def_op('SLICE+2', 32) 169 | def_op('SLICE+3', 33) 170 | 171 | def_op('STORE_SLICE+0', 40) 172 | def_op('STORE_SLICE+1', 41) 173 | def_op('STORE_SLICE+2', 42) 174 | def_op('STORE_SLICE+3', 43) 175 | 176 | def_op('DELETE_SLICE+0', 50) 177 | def_op('DELETE_SLICE+1', 51) 178 | def_op('DELETE_SLICE+2', 52) 179 | def_op('DELETE_SLICE+3', 53) 180 | 181 | def_op('STORE_SUBSCR', 60) 182 | def_op('DELETE_SUBSCR', 61) 183 | 184 | def_op('BINARY_LSHIFT', 62) 185 | def_op('BINARY_RSHIFT', 63) 186 | def_op('BINARY_AND', 64) 187 | def_op('BINARY_XOR', 65) 188 | def_op('BINARY_OR', 66) 189 | 190 | def_op('PRINT_EXPR', 70) 191 | def_op('PRINT_ITEM', 71) 192 | def_op('PRINT_NEWLINE', 72) 193 | 194 | def_op('BREAK_LOOP', 80) 195 | 196 | def_op('LOAD_LOCALS', 82) 197 | def_op('RETURN_VALUE', 83) 198 | 199 | def_op('EXEC_STMT', 85) 200 | 201 | def_op('POP_BLOCK', 87) 202 | def_op('END_FINALLY', 88) 203 | def_op('BUILD_CLASS', 89) 204 | 205 | HAVE_ARGUMENT = 90 # Opcodes from here have an argument: 206 | 207 | name_op('STORE_NAME', 90) # Index in name list 208 | name_op('DELETE_NAME', 91) # "" 209 | def_op('UNPACK_TUPLE', 92) # Number of tuple items 210 | def_op('UNPACK_LIST', 93) # Number of list items 211 | name_op('STORE_ATTR', 95) # Index in name list 212 | name_op('DELETE_ATTR', 96) # "" 213 | name_op('STORE_GLOBAL', 97) # "" 214 | name_op('DELETE_GLOBAL', 98) # "" 215 | 216 | def_op('LOAD_CONST', 100) # Index in const list 217 | hasconst.append(100) 218 | name_op('LOAD_NAME', 101) # Index in name list 219 | def_op('BUILD_TUPLE', 102) # Number of tuple items 220 | def_op('BUILD_LIST', 103) # Number of list items 221 | def_op('BUILD_MAP', 104) # Always zero for now 222 | name_op('LOAD_ATTR', 105) # Index in name list 223 | def_op('COMPARE_OP', 106) # Comparison operator 224 | hascompare.append(106) 225 | name_op('IMPORT_NAME', 107) # Index in name list 226 | name_op('IMPORT_FROM', 108) # Index in name list 227 | 228 | jrel_op('JUMP_FORWARD', 110) # Number of bytes to skip 229 | jrel_op('JUMP_IF_FALSE', 111) # "" 230 | jrel_op('JUMP_IF_TRUE', 112) # "" 231 | jabs_op('JUMP_ABSOLUTE', 113) # Target byte offset from beginning of code 232 | jrel_op('FOR_LOOP', 114) # Number of bytes to skip 233 | 234 | name_op('LOAD_GLOBAL', 116) # Index in name list 235 | 236 | jrel_op('SETUP_LOOP', 120) # Distance to target address 237 | jrel_op('SETUP_EXCEPT', 121) # "" 238 | jrel_op('SETUP_FINALLY', 122) # "" 239 | 240 | def_op('LOAD_FAST', 124) # Local variable number 241 | haslocal.append(124) 242 | def_op('STORE_FAST', 125) # Local variable number 243 | haslocal.append(125) 244 | def_op('DELETE_FAST', 126) # Local variable number 245 | haslocal.append(126) 246 | 247 | def_op('SET_LINENO', 127) # Current line number 248 | SET_LINENO = 127 249 | 250 | def_op('RAISE_VARARGS', 130) 251 | def_op('CALL_FUNCTION', 131) 252 | def_op('MAKE_FUNCTION', 132) 253 | def_op('BUILD_SLICE', 133) 254 | -------------------------------------------------------------------------------- /decompyle/decompyle/dis_23.py: -------------------------------------------------------------------------------- 1 | """Disassembler of Python byte code into mnemonics.""" 2 | 3 | import sys 4 | import types 5 | 6 | from opcode_25 import * 7 | from opcode_25 import __all__ as _opcodes_all 8 | 9 | __all__ = ["dis","disassemble","distb","disco"] + _opcodes_all 10 | del _opcodes_all 11 | 12 | def dis(x=None): 13 | """Disassemble classes, methods, functions, or code. 14 | 15 | With no argument, disassemble the last traceback. 16 | 17 | """ 18 | if x is None: 19 | distb() 20 | return 21 | if type(x) is types.InstanceType: 22 | x = x.__class__ 23 | if hasattr(x, 'im_func'): 24 | x = x.im_func 25 | if hasattr(x, 'func_code'): 26 | x = x.func_code 27 | if hasattr(x, '__dict__'): 28 | items = x.__dict__.items() 29 | items.sort() 30 | for name, x1 in items: 31 | if type(x1) in (types.MethodType, 32 | types.FunctionType, 33 | types.CodeType, 34 | types.ClassType): 35 | print "Disassembly of %s:" % name 36 | try: 37 | dis(x1) 38 | except TypeError, msg: 39 | print "Sorry:", msg 40 | print 41 | elif hasattr(x, 'co_code'): 42 | disassemble(x) 43 | elif isinstance(x, str): 44 | disassemble_string(x) 45 | else: 46 | raise TypeError, \ 47 | "don't know how to disassemble %s objects" % \ 48 | type(x).__name__ 49 | 50 | def distb(tb=None): 51 | """Disassemble a traceback (default: last traceback).""" 52 | if tb is None: 53 | try: 54 | tb = sys.last_traceback 55 | except AttributeError: 56 | raise RuntimeError, "no last traceback to disassemble" 57 | while tb.tb_next: tb = tb.tb_next 58 | disassemble(tb.tb_frame.f_code, tb.tb_lasti) 59 | 60 | def disassemble(co, lasti=-1): 61 | """Disassemble a code object.""" 62 | code = co.co_code 63 | 64 | byte_increments = [ord(c) for c in co.co_lnotab[0::2]] 65 | line_increments = [ord(c) for c in co.co_lnotab[1::2]] 66 | table_length = len(byte_increments) # == len(line_increments) 67 | 68 | lineno = co.co_firstlineno 69 | table_index = 0 70 | while (table_index < table_length 71 | and byte_increments[table_index] == 0): 72 | lineno += line_increments[table_index] 73 | table_index += 1 74 | addr = 0 75 | line_incr = 0 76 | 77 | labels = findlabels(code) 78 | n = len(code) 79 | i = 0 80 | extended_arg = 0 81 | free = None 82 | while i < n: 83 | c = code[i] 84 | op = ord(c) 85 | 86 | if i >= addr: 87 | lineno += line_incr 88 | while table_index < table_length: 89 | addr += byte_increments[table_index] 90 | line_incr = line_increments[table_index] 91 | table_index += 1 92 | if line_incr: 93 | break 94 | else: 95 | addr = sys.maxint 96 | if i > 0: 97 | print 98 | print "%3d"%lineno, 99 | else: 100 | print ' ', 101 | 102 | if i == lasti: print '-->', 103 | else: print ' ', 104 | if i in labels: print '>>', 105 | else: print ' ', 106 | print `i`.rjust(4), 107 | print opname[op].ljust(20), 108 | i = i+1 109 | if op >= HAVE_ARGUMENT: 110 | oparg = ord(code[i]) + ord(code[i+1])*256 + extended_arg 111 | extended_arg = 0 112 | i = i+2 113 | if op == EXTENDED_ARG: 114 | extended_arg = oparg*65536L 115 | print `oparg`.rjust(5), 116 | if op in hasconst: 117 | print '(' + `co.co_consts[oparg]` + ')', 118 | elif op in hasname: 119 | print '(' + co.co_names[oparg] + ')', 120 | elif op in hasjrel: 121 | print '(to ' + `i + oparg` + ')', 122 | elif op in haslocal: 123 | print '(' + co.co_varnames[oparg] + ')', 124 | elif op in hascompare: 125 | print '(' + cmp_op[oparg] + ')', 126 | elif op in hasfree: 127 | if free is None: 128 | free = co.co_cellvars + co.co_freevars 129 | print '(' + free[oparg] + ')', 130 | print 131 | 132 | def disassemble_string(code, lasti=-1, varnames=None, names=None, 133 | constants=None): 134 | labels = findlabels(code) 135 | n = len(code) 136 | i = 0 137 | while i < n: 138 | c = code[i] 139 | op = ord(c) 140 | if op == opmap['SET_LINENO'] and i > 0: 141 | print # Extra blank line 142 | if i == lasti: print '-->', 143 | else: print ' ', 144 | if i in labels: print '>>', 145 | else: print ' ', 146 | print `i`.rjust(4), 147 | print opname[op].ljust(15), 148 | i = i+1 149 | if op >= HAVE_ARGUMENT: 150 | oparg = ord(code[i]) + ord(code[i+1])*256 151 | i = i+2 152 | print `oparg`.rjust(5), 153 | if op in hasconst: 154 | if constants: 155 | print '(' + `constants[oparg]` + ')', 156 | else: 157 | print '(%d)'%oparg, 158 | elif op in hasname: 159 | if names is not None: 160 | print '(' + names[oparg] + ')', 161 | else: 162 | print '(%d)'%oparg, 163 | elif op in hasjrel: 164 | print '(to ' + `i + oparg` + ')', 165 | elif op in haslocal: 166 | if varnames: 167 | print '(' + varnames[oparg] + ')', 168 | else: 169 | print '(%d)' % oparg, 170 | elif op in hascompare: 171 | print '(' + cmp_op[oparg] + ')', 172 | print 173 | 174 | disco = disassemble # XXX For backwards compatibility 175 | 176 | def findlabels(code): 177 | """Detect all offsets in a byte code which are jump targets. 178 | 179 | Return the list of offsets. 180 | 181 | """ 182 | labels = [] 183 | n = len(code) 184 | i = 0 185 | while i < n: 186 | c = code[i] 187 | op = ord(c) 188 | i = i+1 189 | if op >= HAVE_ARGUMENT: 190 | oparg = ord(code[i]) + ord(code[i+1])*256 191 | i = i+2 192 | label = -1 193 | if op in hasjrel: 194 | label = i+oparg 195 | elif op in hasjabs: 196 | label = oparg 197 | if label >= 0: 198 | if label not in labels: 199 | labels.append(label) 200 | return labels 201 | 202 | 203 | def _test(): 204 | """Simple test program to disassemble a file.""" 205 | if sys.argv[1:]: 206 | if sys.argv[2:]: 207 | sys.stderr.write("usage: python dis.py [-|file]\n") 208 | sys.exit(2) 209 | fn = sys.argv[1] 210 | if not fn or fn == "-": 211 | fn = None 212 | else: 213 | fn = None 214 | if fn is None: 215 | f = sys.stdin 216 | else: 217 | f = open(fn) 218 | source = f.read() 219 | if fn is not None: 220 | f.close() 221 | else: 222 | fn = "" 223 | code = compile(source, fn, "exec") 224 | dis(code) 225 | 226 | if __name__ == "__main__": 227 | _test() 228 | -------------------------------------------------------------------------------- /decompyle/decompyle/Scanner.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 1999 John Aycock 2 | # Copyright (c) 2000-2002 by hartmut Goebel 3 | # 4 | # See main module for license. 5 | # 6 | 7 | __all__ = ['Token', 'Scanner', 'getscanner'] 8 | 9 | import types 10 | import dis 11 | import re 12 | 13 | class Token: 14 | """ 15 | Class representing a byte-code token. 16 | 17 | A byte-code token is equivalent to the contents of one line 18 | as output by dis.dis(). 19 | """ 20 | def __init__(self, type, attr=None, pattr=None, offset=-1): 21 | self.type = intern(type) 22 | self.attr = attr #oparg 23 | self.pattr = pattr # oparg's data,if load_const,this is string 24 | self.offset = offset # pos 25 | 26 | def __cmp__(self, o): 27 | if isinstance(o, Token): 28 | # both are tokens: compare type and pattr 29 | return cmp(self.type, o.type) or cmp(self.pattr, o.pattr) 30 | else: 31 | return cmp(self.type, o) 32 | 33 | def __repr__(self): return str(self.type) 34 | def __str__(self): 35 | pattr = self.pattr or '' 36 | return '%s\t%-17s %r' % (self.offset, self.type, pattr) 37 | def __hash__(self): return hash(self.type) 38 | def __getitem__(self, i): raise IndexError 39 | 40 | 41 | class Code: 42 | """ 43 | Class for representing code-objects. 44 | 45 | This is similar to the original code object, but additionally 46 | the diassembled code is stored in the attribute '_tokens'. 47 | """ 48 | def __init__(self, co, scanner): 49 | for i in dir(co): 50 | if i.startswith('co_'): 51 | setattr(self, i, getattr(co, i)) 52 | self._tokens, self._customize = scanner.disassemble(co) 53 | 54 | class Scanner: 55 | def __init__(self, version): 56 | self.__version = version 57 | 58 | self.resetTokenClass() 59 | 60 | self.JUMP_OPs = map(lambda op: dis.opname[op], 61 | dis.hasjrel + dis.hasjabs) 62 | 63 | def setShowAsm(self, showasm, out=None): 64 | self.showasm = showasm 65 | self.out = out 66 | 67 | def setTokenClass(self, tokenClass): 68 | assert type(tokenClass) == types.ClassType 69 | self.Token = tokenClass 70 | 71 | def resetTokenClass(self): 72 | self.setTokenClass(Token) 73 | 74 | def disassemble(self, co): 75 | """ 76 | Disassemble a code object, returning a list of 'Token'. 77 | 78 | The main part of this procedure is modelled after 79 | dis.disassemble(). 80 | """ 81 | rv = [] 82 | #other info such ad make_fun_1... 83 | #because create function use other info 84 | customize = {} 85 | Token = self.Token # shortcut 86 | 87 | code = co.co_code 88 | cf = self.find_jump_targets(code) 89 | n = len(code) 90 | i = 0 91 | extended_arg = 0 92 | free = None 93 | while i < n: 94 | offset = i 95 | if cf.has_key(offset): 96 | for j in range(cf[offset]): 97 | rv.append(Token('COME_FROM', 98 | offset="%s_%d" % (offset, j) )) 99 | 100 | c = code[i] 101 | op = ord(c) 102 | opname = dis.opname[op] 103 | i += 1 104 | oparg = None; pattr = None 105 | if op >= dis.HAVE_ARGUMENT: 106 | oparg = ord(code[i]) + ord(code[i+1]) * 256 + extended_arg 107 | extended_arg = 0 108 | i += 2 109 | if op == dis.EXTENDED_ARG: 110 | extended_arg = oparg * 65536L 111 | if op in dis.hasconst: 112 | const = co.co_consts[oparg] 113 | if type(const) == types.CodeType: 114 | oparg = const 115 | lambdaReg= re.compile(r'') 116 | if lambdaReg.match(const.co_name): 117 | assert opname == 'LOAD_CONST' 118 | opname = 'LOAD_LAMBDA' 119 | # verify uses 'pattr' for comparism, since 'attr' 120 | # now holds Code(const) and thus can not be used 121 | # for comparism (todo: think about changing this) 122 | #pattr = 'code_object @ 0x%x %s->%s' %\ 123 | # (id(const), const.co_filename, const.co_name) 124 | pattr = 'code_object ' + const.co_name 125 | else: 126 | pattr = const 127 | elif op in dis.hasname: 128 | pattr = co.co_names[oparg] 129 | elif op in dis.hasjrel: 130 | pattr = repr(i + oparg) 131 | elif op in dis.hasjabs: 132 | pattr = repr(oparg) 133 | elif op in dis.haslocal: 134 | pattr = co.co_varnames[oparg] 135 | elif op in dis.hascompare: 136 | pattr = dis.cmp_op[oparg] 137 | elif op in dis.hasfree: 138 | if free is None: 139 | free = co.co_cellvars + co.co_freevars 140 | pattr = free[oparg] 141 | 142 | if opname == 'SET_LINENO': 143 | continue 144 | elif opname in ('BUILD_LIST', 'BUILD_TUPLE', 'BUILD_SLICE', 145 | 'UNPACK_LIST', 'UNPACK_TUPLE', 'UNPACK_SEQUENCE', 146 | 'MAKE_FUNCTION', 'CALL_FUNCTION', 'MAKE_CLOSURE', 147 | 'CALL_FUNCTION_VAR', 'CALL_FUNCTION_KW', 148 | 'CALL_FUNCTION_VAR_KW', 'DUP_TOPX', 149 | ): 150 | opname = '%s_%d' % (opname, oparg) 151 | customize[opname] = oparg 152 | 153 | rv.append(Token(opname, oparg, pattr, offset)) 154 | 155 | if self.showasm: 156 | out = self.out # shortcut 157 | for t in rv: 158 | print >>out, t 159 | print >>out 160 | 161 | return rv, customize 162 | 163 | 164 | def find_jump_targets(self, code): 165 | """ 166 | Detect all offsets in a byte code which are jump targets. 167 | 168 | Return the list of offsets. 169 | 170 | This procedure is modelled after dis.findlables(), but here 171 | for each target the number of jumps are counted. 172 | """ 173 | HAVE_ARGUMENT = dis.HAVE_ARGUMENT 174 | hasjrel = dis.hasjrel 175 | #hasjabs = dis.hasjabs 176 | 177 | targets = {} 178 | n = len(code) 179 | i = 0 180 | while i < n: 181 | c = code[i] 182 | op = ord(c) 183 | #print "do " + dis.opname[op] 184 | i += 1 185 | if op >= HAVE_ARGUMENT: 186 | oparg = ord(code[i]) + ord(code[i+1]) * 256 187 | i += 2 188 | label = -1 189 | if op in hasjrel: 190 | label = i + oparg 191 | # todo: absolut jumps 192 | #elif op in hasjabs: 193 | # label = oparg 194 | if label >= 0: 195 | targets[label] = targets.get(label, 0) + 1 196 | return targets 197 | 198 | 199 | __scanners = {} 200 | 201 | def getscanner(version): 202 | if not __scanners.has_key(version): 203 | __scanners[version] = Scanner(version) 204 | return __scanners[version] 205 | 206 | # local variables: 207 | # tab-width: 4 208 | -------------------------------------------------------------------------------- /decompyle/decompyle/dis_25.py: -------------------------------------------------------------------------------- 1 | """Disassembler of Python byte code into mnemonics.""" 2 | 3 | import sys 4 | import types 5 | 6 | from opcode_25 import * 7 | from opcode_25 import __all__ as _opcodes_all 8 | 9 | __all__ = ["dis","disassemble","distb","disco"] + _opcodes_all 10 | del _opcodes_all 11 | 12 | def dis(x=None): 13 | """Disassemble classes, methods, functions, or code. 14 | 15 | With no argument, disassemble the last traceback. 16 | 17 | """ 18 | if x is None: 19 | distb() 20 | return 21 | if type(x) is types.InstanceType: 22 | x = x.__class__ 23 | if hasattr(x, 'im_func'): 24 | x = x.im_func 25 | if hasattr(x, 'func_code'): 26 | x = x.func_code 27 | if hasattr(x, '__dict__'): 28 | items = x.__dict__.items() 29 | items.sort() 30 | for name, x1 in items: 31 | if type(x1) in (types.MethodType, 32 | types.FunctionType, 33 | types.CodeType, 34 | types.ClassType): 35 | print "Disassembly of %s:" % name 36 | try: 37 | dis(x1) 38 | except TypeError, msg: 39 | print "Sorry:", msg 40 | print 41 | elif hasattr(x, 'co_code'): 42 | disassemble(x) 43 | elif isinstance(x, str): 44 | disassemble_string(x) 45 | else: 46 | raise TypeError, \ 47 | "don't know how to disassemble %s objects" % \ 48 | type(x).__name__ 49 | 50 | def distb(tb=None): 51 | """Disassemble a traceback (default: last traceback).""" 52 | if tb is None: 53 | try: 54 | tb = sys.last_traceback 55 | except AttributeError: 56 | raise RuntimeError, "no last traceback to disassemble" 57 | while tb.tb_next: tb = tb.tb_next 58 | disassemble(tb.tb_frame.f_code, tb.tb_lasti) 59 | 60 | def disassemble(co, lasti=-1): 61 | """Disassemble a code object.""" 62 | code = co.co_code 63 | labels = findlabels(code) 64 | linestarts = dict(findlinestarts(co)) 65 | n = len(code) 66 | i = 0 67 | extended_arg = 0 68 | free = None 69 | while i < n: 70 | c = code[i] 71 | op = ord(c) 72 | if i in linestarts: 73 | if i > 0: 74 | print 75 | print "%3d" % linestarts[i], 76 | else: 77 | print ' ', 78 | 79 | if i == lasti: print '-->', 80 | else: print ' ', 81 | if i in labels: print '>>', 82 | else: print ' ', 83 | print repr(i).rjust(4), 84 | print opname[op].ljust(20), 85 | i = i+1 86 | if op >= HAVE_ARGUMENT: 87 | oparg = ord(code[i]) + ord(code[i+1])*256 + extended_arg 88 | extended_arg = 0 89 | i = i+2 90 | if op == EXTENDED_ARG: 91 | extended_arg = oparg*65536L 92 | print repr(oparg).rjust(5), 93 | if op in hasconst: 94 | print '(' + repr(co.co_consts[oparg]) + ')', 95 | elif op in hasname: 96 | print '(' + co.co_names[oparg] + ')', 97 | elif op in hasjrel: 98 | print '(to ' + repr(i + oparg) + ')', 99 | elif op in haslocal: 100 | print '(' + co.co_varnames[oparg] + ')', 101 | elif op in hascompare: 102 | print '(' + cmp_op[oparg] + ')', 103 | elif op in hasfree: 104 | if free is None: 105 | free = co.co_cellvars + co.co_freevars 106 | print '(' + free[oparg] + ')', 107 | print 108 | 109 | def disassemble_string(code, lasti=-1, varnames=None, names=None, 110 | constants=None): 111 | labels = findlabels(code) 112 | n = len(code) 113 | i = 0 114 | while i < n: 115 | c = code[i] 116 | op = ord(c) 117 | if i == lasti: print '-->', 118 | else: print ' ', 119 | if i in labels: print '>>', 120 | else: print ' ', 121 | print repr(i).rjust(4), 122 | print opname[op].ljust(15), 123 | i = i+1 124 | if op >= HAVE_ARGUMENT: 125 | oparg = ord(code[i]) + ord(code[i+1])*256 126 | i = i+2 127 | print repr(oparg).rjust(5), 128 | if op in hasconst: 129 | if constants: 130 | print '(' + repr(constants[oparg]) + ')', 131 | else: 132 | print '(%d)'%oparg, 133 | elif op in hasname: 134 | if names is not None: 135 | print '(' + names[oparg] + ')', 136 | else: 137 | print '(%d)'%oparg, 138 | elif op in hasjrel: 139 | print '(to ' + repr(i + oparg) + ')', 140 | elif op in haslocal: 141 | if varnames: 142 | print '(' + varnames[oparg] + ')', 143 | else: 144 | print '(%d)' % oparg, 145 | elif op in hascompare: 146 | print '(' + cmp_op[oparg] + ')', 147 | print 148 | 149 | disco = disassemble # XXX For backwards compatibility 150 | 151 | def findlabels(code): 152 | """Detect all offsets in a byte code which are jump targets. 153 | 154 | Return the list of offsets. 155 | 156 | """ 157 | labels = [] 158 | n = len(code) 159 | i = 0 160 | while i < n: 161 | c = code[i] 162 | op = ord(c) 163 | i = i+1 164 | if op >= HAVE_ARGUMENT: 165 | oparg = ord(code[i]) + ord(code[i+1])*256 166 | i = i+2 167 | label = -1 168 | if op in hasjrel: 169 | label = i+oparg 170 | elif op in hasjabs: 171 | label = oparg 172 | if label >= 0: 173 | if label not in labels: 174 | labels.append(label) 175 | return labels 176 | 177 | def findlinestarts(code): 178 | """Find the offsets in a byte code which are start of lines in the source. 179 | 180 | Generate pairs (offset, lineno) as described in Python/compile.c. 181 | 182 | """ 183 | byte_increments = [ord(c) for c in code.co_lnotab[0::2]] 184 | line_increments = [ord(c) for c in code.co_lnotab[1::2]] 185 | 186 | lastlineno = None 187 | lineno = code.co_firstlineno 188 | addr = 0 189 | for byte_incr, line_incr in zip(byte_increments, line_increments): 190 | if byte_incr: 191 | if lineno != lastlineno: 192 | yield (addr, lineno) 193 | lastlineno = lineno 194 | addr += byte_incr 195 | lineno += line_incr 196 | if lineno != lastlineno: 197 | yield (addr, lineno) 198 | 199 | def _test(): 200 | """Simple test program to disassemble a file.""" 201 | if sys.argv[1:]: 202 | if sys.argv[2:]: 203 | sys.stderr.write("usage: python dis.py [-|file]\n") 204 | sys.exit(2) 205 | fn = sys.argv[1] 206 | if not fn or fn == "-": 207 | fn = None 208 | else: 209 | fn = None 210 | if fn is None: 211 | f = sys.stdin 212 | else: 213 | f = open(fn) 214 | source = f.read() 215 | if fn is not None: 216 | f.close() 217 | else: 218 | fn = "" 219 | code = compile(source, fn, "exec") 220 | dis(code) 221 | 222 | if __name__ == "__main__": 223 | _test() 224 | -------------------------------------------------------------------------------- /decompyle/decompyle/verify.py: -------------------------------------------------------------------------------- 1 | # 2 | # (C) Copyright 2000-2002 by hartmut Goebel 3 | # 4 | # byte-code verifier for decompyle 5 | # 6 | 7 | import types 8 | import decompyle, Scanner 9 | 10 | JUMP_OPs = None 11 | 12 | #--- exceptions --- 13 | 14 | class VerifyCmpError(Exception): 15 | pass 16 | 17 | class CmpErrorConsts(VerifyCmpError): 18 | """Exception to be raised when consts differ.""" 19 | def __init__(self, name, index): 20 | self.name = name 21 | self.index = index 22 | 23 | def __str__(self): 24 | return 'Compare Error within Consts of %s at index %i' % \ 25 | (repr(self.name), self.index) 26 | 27 | class CmpErrorConstsLen(VerifyCmpError): 28 | """Exception to be raised when length of co_consts differs.""" 29 | def __init__(self, name, consts1, consts2): 30 | self.name = name 31 | self.consts = (consts1, consts2) 32 | 33 | def __str__(self): 34 | return 'Consts length differs in %s:\n\n%i:\t%s\n\n%i:\t%s\n\n' % \ 35 | (repr(self.name), 36 | len(self.consts[0]), `self.consts[0]`, 37 | len(self.consts[1]), `self.consts[1]`) 38 | 39 | class CmpErrorCode(VerifyCmpError): 40 | """Exception to be raised when code differs.""" 41 | def __init__(self, name, index, token1, token2): 42 | self.name = name 43 | self.index = index 44 | self.token1 = token1 45 | self.token2 = token2 46 | 47 | def __str__(self): 48 | return 'Code differs in %s at offset %i [%s] != [%s]' % \ 49 | (repr(self.name), self.index, 50 | repr(self.token1), repr(self.token2)) #\ 51 | # + ('%s %s') % (self.token1.pattr, self.token2.pattr) 52 | 53 | class CmpErrorCodeLen(VerifyCmpError): 54 | """Exception to be raised when code length differs.""" 55 | def __init__(self, name, tokens1, tokens2): 56 | self.name = name 57 | self.tokens = [tokens1, tokens2] 58 | 59 | def __str__(self): 60 | return reduce(lambda s,t: "%s%-37s\t%-37s\n" % (s, t[0], t[1]), 61 | map(lambda a,b: (a,b), 62 | self.tokens[0], 63 | self.tokens[1]), 64 | 'Code len differs in %s\n' % str(self.name)) 65 | 66 | class CmpErrorMember(VerifyCmpError): 67 | """Exception to be raised when other members differ.""" 68 | def __init__(self, name, member, data1, data2): 69 | self.name = name 70 | self.member = member 71 | self.data = (data1, data2) 72 | 73 | def __str__(self): 74 | return 'Member %s differs in %s:\n\t%s\n\t%s\n' % \ 75 | (repr(self.member), repr(self.name), 76 | repr(self.data[0]), repr(self.data[1])) 77 | 78 | #--- compare --- 79 | 80 | # these members are ignored 81 | __IGNORE_CODE_MEMBERS__ = ['co_filename', 'co_firstlineno', 'co_lnotab'] 82 | 83 | def cmp_code_objects(version, code_obj1, code_obj2, name=''): 84 | """ 85 | Compare two code-objects. 86 | 87 | This is the main part of this module. 88 | """ 89 | #print code_obj1, type(code_obj2) 90 | assert type(code_obj1) == types.CodeType 91 | assert type(code_obj2) == types.CodeType 92 | #print dir(code_obj1) 93 | if isinstance(code_obj1, object): 94 | # new style classes (Python 2.2) 95 | # assume _both_ code objects to be new stle classes 96 | assert dir(code_obj1) == dir(code_obj2) 97 | else: 98 | # old style classes 99 | assert dir(code_obj1) == code_obj1.__members__ 100 | assert dir(code_obj2) == code_obj2.__members__ 101 | assert code_obj1.__members__ == code_obj2.__members__ 102 | 103 | if name == '__main__': 104 | name = code_obj1.co_name 105 | else: 106 | name = '%s.%s' % (name, code_obj1.co_name) 107 | if name == '.?': name = '__main__' 108 | 109 | if isinstance(code_obj1, object) and cmp(code_obj1, code_obj2): 110 | # use the new style code-classes' __cmp__ method, which 111 | # should be faster and more sophisticated 112 | # if this compare fails, we use the old routine to 113 | # find out, what exactly is nor equal 114 | # if this compare succeds, simply return 115 | #return 116 | pass 117 | 118 | if isinstance(code_obj1, object): 119 | members = filter(lambda x: x.startswith('co_'), dir(code_obj1)) 120 | else: 121 | members = dir(code_obj1); 122 | members.sort(); #members.reverse() 123 | 124 | tokens1 = None 125 | for member in members: 126 | if member in __IGNORE_CODE_MEMBERS__: 127 | pass 128 | elif member == 'co_code': 129 | scanner = Scanner.getscanner(version) 130 | scanner.setShowAsm( showasm=0 ) 131 | global JUMP_OPs 132 | JUMP_OPs = scanner.JUMP_OPs 133 | 134 | # use changed Token class 135 | # we (re)set this here to save exception handling, 136 | # which would get 'unbersichtlich' 137 | scanner.setTokenClass(Token) 138 | try: 139 | # disassemble both code-objects 140 | tokens1,customize = scanner.disassemble(code_obj1) 141 | del customize # save memory 142 | tokens2,customize = scanner.disassemble(code_obj2) 143 | del customize # save memory 144 | finally: 145 | scanner.resetTokenClass() # restore Token class 146 | 147 | # compare length 148 | if len(tokens1) != len(tokens2): 149 | raise CmpErrorCodeLen(name, tokens1, tokens2) 150 | # compare contents 151 | #print len(tokens1), type(tokens1), type(tokens2) 152 | for i in xrange(len(tokens1)): 153 | if tokens1[i] != tokens2[i]: 154 | #print '-->', i, type(tokens1[i]), type(tokens2[i]) 155 | raise CmpErrorCode(name, i, tokens1[i], 156 | tokens2[i]) 157 | del tokens1, tokens2 # save memory 158 | elif member == 'co_consts': 159 | # compare length 160 | if len(code_obj1.co_consts) != len(code_obj2.co_consts): 161 | raise CmpErrorConstsLen(name, code_obj1.co_consts ,code_obj2.co_consts) 162 | # compare contents 163 | for idx in xrange(len(code_obj1.co_consts)): 164 | const1 = code_obj1.co_consts[idx] 165 | const2 = code_obj2.co_consts[idx] 166 | ## print code_obj1.co_consts[idx], '\t', 167 | ## print code_obj2.co_consts[idx] 168 | # same type? 169 | if type(const1) != type(const2): 170 | raise CmpErrorContType(name, idx) 171 | if type(const1) == types.CodeType: 172 | # code object -> recursive compare 173 | cmp_code_objects(version, const1, 174 | const2, name=name) 175 | elif cmp(const1, const2) != 0: 176 | # content differs 177 | raise CmpErrorConsts(name, idx) 178 | else: 179 | # all other members must be equal 180 | if getattr(code_obj1, member) != getattr(code_obj2, member): 181 | raise CmpErrorMember(name, member, 182 | getattr(code_obj1,member), 183 | getattr(code_obj2,member)) 184 | 185 | class Token(Scanner.Token): 186 | """Token class with changed semantics for 'cmp()'.""" 187 | 188 | def __cmp__(self, o): 189 | t = self.type # shortcut 190 | if t in JUMP_OPs: 191 | # ignore offset 192 | return cmp(t, o.type) 193 | else: 194 | return cmp(t, o.type) \ 195 | or cmp(self.pattr, o.pattr) 196 | 197 | def __repr__(self): 198 | return '%s %s (%s)' % (str(self.type), str(self.attr), 199 | repr(self.pattr)) 200 | 201 | 202 | def compare_code_with_srcfile(pyc_filename, src_filename): 203 | """Compare a .pyc with a source code file.""" 204 | version, code_obj1 = decompyle._load_module(pyc_filename) 205 | code_obj2 = decompyle._load_file(src_filename) 206 | cmp_code_objects(version, code_obj1, code_obj2) 207 | 208 | def compare_files(pyc_filename1, pyc_filename2): 209 | """Compare two .pyc files.""" 210 | version, code_obj1 = decompyle._load_module(pyc_filename1) 211 | version, code_obj2 = decompyle._load_module(pyc_filename2) 212 | cmp_code_objects(version, code_obj1, code_obj2) 213 | 214 | if __name__ == '__main__': 215 | t1 = Token('LOAD_CONST', None, 'code_object _expandLang', 52) 216 | t2 = Token('LOAD_CONST', -421, 'code_object _expandLang', 55) 217 | print `t1` 218 | print `t2` 219 | print cmp(t1, t2), cmp(t1.type, t2.type), cmp(t1.attr, t2.attr) 220 | 221 | # local variables: 222 | # tab-width: 4 223 | -------------------------------------------------------------------------------- /decompyle/decompyle/dis_16.py: -------------------------------------------------------------------------------- 1 | """Disassembler of Python byte code into mnemonics.""" 2 | 3 | import sys 4 | import string 5 | import types 6 | 7 | def dis(x=None): 8 | """Disassemble classes, methods, functions, or code. 9 | 10 | With no argument, disassemble the last traceback. 11 | 12 | """ 13 | if not x: 14 | distb() 15 | return 16 | if type(x) is types.InstanceType: 17 | x = x.__class__ 18 | if hasattr(x, '__dict__'): 19 | items = x.__dict__.items() 20 | items.sort() 21 | for name, x1 in items: 22 | if type(x1) in (types.MethodType, 23 | types.FunctionType, 24 | types.CodeType): 25 | print "Disassembly of %s:" % name 26 | try: 27 | dis(x1) 28 | except TypeError, msg: 29 | print "Sorry:", msg 30 | print 31 | else: 32 | if hasattr(x, 'im_func'): 33 | x = x.im_func 34 | if hasattr(x, 'func_code'): 35 | x = x.func_code 36 | if hasattr(x, 'co_code'): 37 | disassemble(x) 38 | else: 39 | raise TypeError, \ 40 | "don't know how to disassemble %s objects" % \ 41 | type(x).__name__ 42 | 43 | def distb(tb=None): 44 | """Disassemble a traceback (default: last traceback).""" 45 | if not tb: 46 | try: 47 | tb = sys.last_traceback 48 | except AttributeError: 49 | raise RuntimeError, "no last traceback to disassemble" 50 | while tb.tb_next: tb = tb.tb_next 51 | disassemble(tb.tb_frame.f_code, tb.tb_lasti) 52 | 53 | def disassemble(co, lasti=-1): 54 | """Disassemble a code object.""" 55 | code = co.co_code 56 | labels = findlabels(code) 57 | n = len(code) 58 | i = 0 59 | while i < n: 60 | c = code[i] 61 | op = ord(c) 62 | if op == SET_LINENO and i > 0: print # Extra blank line 63 | if i == lasti: print '-->', 64 | else: print ' ', 65 | if i in labels: print '>>', 66 | else: print ' ', 67 | print string.rjust(`i`, 4), 68 | print string.ljust(opname[op], 20), 69 | i = i+1 70 | if op >= HAVE_ARGUMENT: 71 | oparg = ord(code[i]) + ord(code[i+1])*256 72 | i = i+2 73 | print string.rjust(`oparg`, 5), 74 | if op in hasconst: 75 | print '(' + `co.co_consts[oparg]` + ')', 76 | elif op in hasname: 77 | print '(' + co.co_names[oparg] + ')', 78 | elif op in hasjrel: 79 | print '(to ' + `i + oparg` + ')', 80 | elif op in haslocal: 81 | print '(' + co.co_varnames[oparg] + ')', 82 | elif op in hascompare: 83 | print '(' + cmp_op[oparg] + ')', 84 | print 85 | 86 | disco = disassemble # XXX For backwards compatibility 87 | 88 | def findlabels(code): 89 | """Detect all offsets in a byte code which are jump targets. 90 | 91 | Return the list of offsets. 92 | 93 | """ 94 | labels = [] 95 | n = len(code) 96 | i = 0 97 | while i < n: 98 | c = code[i] 99 | op = ord(c) 100 | i = i+1 101 | if op >= HAVE_ARGUMENT: 102 | oparg = ord(code[i]) + ord(code[i+1])*256 103 | i = i+2 104 | label = -1 105 | if op in hasjrel: 106 | label = i+oparg 107 | elif op in hasjabs: 108 | label = oparg 109 | if label >= 0: 110 | if label not in labels: 111 | labels.append(label) 112 | return labels 113 | 114 | cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is', 115 | 'is not', 'exception match', 'BAD') 116 | 117 | hasconst = [] 118 | hasname = [] 119 | hasjrel = [] 120 | hasjabs = [] 121 | haslocal = [] 122 | hascompare = [] 123 | 124 | opname = [''] * 256 125 | for op in range(256): opname[op] = '<' + `op` + '>' 126 | 127 | def def_op(name, op): 128 | opname[op] = name 129 | 130 | def name_op(name, op): 131 | opname[op] = name 132 | hasname.append(op) 133 | 134 | def jrel_op(name, op): 135 | opname[op] = name 136 | hasjrel.append(op) 137 | 138 | def jabs_op(name, op): 139 | opname[op] = name 140 | hasjabs.append(op) 141 | 142 | # Instruction opcodes for compiled code 143 | 144 | def_op('STOP_CODE', 0) 145 | def_op('POP_TOP', 1) 146 | def_op('ROT_TWO', 2) 147 | def_op('ROT_THREE', 3) 148 | def_op('DUP_TOP', 4) 149 | 150 | def_op('UNARY_POSITIVE', 10) 151 | def_op('UNARY_NEGATIVE', 11) 152 | def_op('UNARY_NOT', 12) 153 | def_op('UNARY_CONVERT', 13) 154 | 155 | def_op('UNARY_INVERT', 15) 156 | 157 | def_op('BINARY_POWER', 19) 158 | 159 | def_op('BINARY_MULTIPLY', 20) 160 | def_op('BINARY_DIVIDE', 21) 161 | def_op('BINARY_MODULO', 22) 162 | def_op('BINARY_ADD', 23) 163 | def_op('BINARY_SUBTRACT', 24) 164 | def_op('BINARY_SUBSCR', 25) 165 | 166 | def_op('SLICE+0', 30) 167 | def_op('SLICE+1', 31) 168 | def_op('SLICE+2', 32) 169 | def_op('SLICE+3', 33) 170 | 171 | def_op('STORE_SLICE+0', 40) 172 | def_op('STORE_SLICE+1', 41) 173 | def_op('STORE_SLICE+2', 42) 174 | def_op('STORE_SLICE+3', 43) 175 | 176 | def_op('DELETE_SLICE+0', 50) 177 | def_op('DELETE_SLICE+1', 51) 178 | def_op('DELETE_SLICE+2', 52) 179 | def_op('DELETE_SLICE+3', 53) 180 | 181 | def_op('STORE_SUBSCR', 60) 182 | def_op('DELETE_SUBSCR', 61) 183 | 184 | def_op('BINARY_LSHIFT', 62) 185 | def_op('BINARY_RSHIFT', 63) 186 | def_op('BINARY_AND', 64) 187 | def_op('BINARY_XOR', 65) 188 | def_op('BINARY_OR', 66) 189 | 190 | def_op('PRINT_EXPR', 70) 191 | def_op('PRINT_ITEM', 71) 192 | def_op('PRINT_NEWLINE', 72) 193 | 194 | def_op('BREAK_LOOP', 80) 195 | 196 | def_op('LOAD_LOCALS', 82) 197 | def_op('RETURN_VALUE', 83) 198 | 199 | def_op('EXEC_STMT', 85) 200 | 201 | def_op('POP_BLOCK', 87) 202 | def_op('END_FINALLY', 88) 203 | def_op('BUILD_CLASS', 89) 204 | 205 | HAVE_ARGUMENT = 90 # Opcodes from here have an argument: 206 | 207 | name_op('STORE_NAME', 90) # Index in name list 208 | name_op('DELETE_NAME', 91) # "" 209 | def_op('UNPACK_TUPLE', 92) # Number of tuple items 210 | def_op('UNPACK_LIST', 93) # Number of list items 211 | name_op('STORE_ATTR', 95) # Index in name list 212 | name_op('DELETE_ATTR', 96) # "" 213 | name_op('STORE_GLOBAL', 97) # "" 214 | name_op('DELETE_GLOBAL', 98) # "" 215 | 216 | def_op('LOAD_CONST', 100) # Index in const list 217 | hasconst.append(100) 218 | name_op('LOAD_NAME', 101) # Index in name list 219 | def_op('BUILD_TUPLE', 102) # Number of tuple items 220 | def_op('BUILD_LIST', 103) # Number of list items 221 | def_op('BUILD_MAP', 104) # Always zero for now 222 | name_op('LOAD_ATTR', 105) # Index in name list 223 | def_op('COMPARE_OP', 106) # Comparison operator 224 | hascompare.append(106) 225 | name_op('IMPORT_NAME', 107) # Index in name list 226 | name_op('IMPORT_FROM', 108) # Index in name list 227 | 228 | jrel_op('JUMP_FORWARD', 110) # Number of bytes to skip 229 | jrel_op('JUMP_IF_FALSE', 111) # "" 230 | jrel_op('JUMP_IF_TRUE', 112) # "" 231 | jabs_op('JUMP_ABSOLUTE', 113) # Target byte offset from beginning of code 232 | jrel_op('FOR_LOOP', 114) # Number of bytes to skip 233 | 234 | name_op('LOAD_GLOBAL', 116) # Index in name list 235 | 236 | jrel_op('SETUP_LOOP', 120) # Distance to target address 237 | jrel_op('SETUP_EXCEPT', 121) # "" 238 | jrel_op('SETUP_FINALLY', 122) # "" 239 | 240 | def_op('LOAD_FAST', 124) # Local variable number 241 | haslocal.append(124) 242 | def_op('STORE_FAST', 125) # Local variable number 243 | haslocal.append(125) 244 | def_op('DELETE_FAST', 126) # Local variable number 245 | haslocal.append(126) 246 | 247 | def_op('SET_LINENO', 127) # Current line number 248 | SET_LINENO = 127 249 | 250 | def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3) 251 | def_op('CALL_FUNCTION', 131) # #args + (#kwargs << 8) 252 | def_op('MAKE_FUNCTION', 132) # Number of args with default values 253 | def_op('BUILD_SLICE', 133) # Number of items 254 | 255 | def_op('CALL_FUNCTION_VAR', 140) # #args + (#kwargs << 8) 256 | def_op('CALL_FUNCTION_KW', 141) # #args + (#kwargs << 8) 257 | def_op('CALL_FUNCTION_VAR_KW', 142) # #args + (#kwargs << 8) 258 | 259 | 260 | def _test(): 261 | """Simple test program to disassemble a file.""" 262 | if sys.argv[1:]: 263 | if sys.argv[2:]: 264 | sys.stderr.write("usage: python dis.py [-|file]\n") 265 | sys.exit(2) 266 | fn = sys.argv[1] 267 | if not fn or fn == "-": 268 | fn = None 269 | else: 270 | fn = None 271 | if not fn: 272 | f = sys.stdin 273 | else: 274 | f = open(fn) 275 | source = f.read() 276 | if fn: 277 | f.close() 278 | else: 279 | fn = "" 280 | code = compile(source, fn, "exec") 281 | dis(code) 282 | 283 | if __name__ == "__main__": 284 | _test() 285 | -------------------------------------------------------------------------------- /decompyle/decompyle/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 1999 John Aycock 2 | # Copyright (c) 2000 by hartmut Goebel 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining 5 | # a copy of this software and associated documentation files (the 6 | # "Software"), to deal in the Software without restriction, including 7 | # without limitation the rights to use, copy, modify, merge, publish, 8 | # distribute, sublicense, and/or sell copies of the Software, and to 9 | # permit persons to whom the Software is furnished to do so, subject to 10 | # the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be 13 | # included in all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | # 23 | # See the file 'CHANGES' for a list of changes 24 | # 25 | # NB. This is not a masterpiece of software, but became more like a hack. 26 | # Probably a complete rewrite would be sensefull. hG/2000-12-27 27 | # 28 | 29 | import sys, types, os 30 | import Scanner, Walker, verify 31 | from compiler import compile 32 | 33 | __all__ = ['decompyle_file', 'decompyle_file', 'main'] 34 | 35 | TABWIDTH=4 36 | 37 | def _load_file(filename): 38 | """ 39 | load a Python source file and compile it to byte-code 40 | 41 | _load_module(filename: string): code_object 42 | 43 | filename: name of file containing Python source code 44 | (normally a .py) 45 | code_object: code_object compiled from this source code 46 | 47 | This function does NOT write any file! 48 | """ 49 | fp = open(filename, 'rb') 50 | source = fp.read() 51 | source = source.replace('\r\n','\n'); 52 | source = source +'\n' 53 | try: 54 | co = compile(source, filename, 'exec') 55 | except SyntaxError: 56 | print >> sys.stderr, '>>Syntax error in', filename 57 | raise 58 | fp.close() 59 | return co 60 | 61 | def _load_module(filename): 62 | """ 63 | load a module without importing it 64 | _load_module(filename: string): code_object 65 | 66 | filename: name of file containing Python byte-code object 67 | (normally a .pyc) 68 | code_object: code_object from this file 69 | """ 70 | import imp, marshal 71 | fp = open(filename, 'rb') 72 | magic = fp.read(4) 73 | if magic <> imp.get_magic(): 74 | raise "not correct magic numbel" 75 | version = '%d.%d' % sys.version_info[:2] 76 | 77 | #print version 78 | fp.read(4) # timestamp 79 | co = marshal.load(fp) 80 | fp.close() 81 | return version, co 82 | 83 | def decompyle(version, co, out=None, showasm=0, showast=0): 84 | """ 85 | diassembles a given code block 'co' 86 | """ 87 | assert type(co) == types.CodeType 88 | 89 | # store final output stream for case of error 90 | __real_out = out or sys.stdout 91 | 92 | scanner = Scanner.getscanner(version) 93 | scanner.setShowAsm(showasm, out) 94 | tokens, customize = scanner.disassemble(co) 95 | 96 | # Build AST from disassembly. 97 | walker = Walker.Walker(out, scanner, showast=showast) 98 | try: 99 | ast = walker.build_ast(tokens, customize) 100 | except Walker.ParserError, e : # parser failed, dump disassembly 101 | print >>__real_out, e 102 | raise 103 | 104 | del tokens # save memory 105 | 106 | if not showasm and not showast: 107 | # add a hint for emacs 108 | print >> out, '# emacs-mode: -*- python-*-' 109 | 110 | # convert leading '__doc__ = "..." into doc string 111 | assert ast == 'stmts' 112 | if ast[0] == Walker.ASSIGN_DOC_STRING(co.co_consts[0]): 113 | walker.print_docstring('', co.co_consts[0]) 114 | del ast[0] 115 | if ast[-1] == Walker.RETURN_NONE: 116 | ast.pop() # remove last node 117 | #todo: if empty, add 'pass' 118 | 119 | walker.gen_source(ast, customize) 120 | 121 | if not showasm and not showast: 122 | # add another hint for emacs 123 | print >> out, '# local variables:' 124 | print >> out, '# tab-width:', TABWIDTH 125 | 126 | 127 | def decompyle_file(filename, outstream=None, showasm=0, showast=0): 128 | """ 129 | decompile Python byte-code file (.pyc) 130 | """ 131 | version, co = _load_module(filename) 132 | decompyle(version, co, out=outstream, showasm=showasm, showast=showast) 133 | co = None 134 | 135 | #---- main ------- 136 | 137 | if sys.platform.startswith('linux') and os.uname()[2][:2] == '2.': 138 | def __memUsage(): 139 | mi = open('/proc/self/stat', 'r') 140 | mu = mi.readline().split()[22] 141 | #return mu 142 | mi.close() 143 | return int(mu) / 1000000 144 | else: 145 | def __memUsage(): 146 | return '' 147 | 148 | def main(in_base, out_base, files, outfile=None, 149 | showasm=0, showast=0, do_verify=0): 150 | """ 151 | in_base base directory for input files 152 | out_base base directory for output files (ignored when 153 | files list of filenames to be decompyles (relative to src_base) 154 | outfile write output to this filename (overwrites out_base) 155 | 156 | For rediecting output to 157 | - outfile= (out_base is ignored) 158 | - files below out_base out_base=... 159 | - stdout out_base=None, outfile=None 160 | """ 161 | def _get_outstream(outfile): 162 | dir = os.path.dirname(outfile) 163 | failed_file = outfile + '_failed' 164 | if os.path.exists(failed_file): os.remove(failed_file) 165 | if not os.path.exists(dir): 166 | try: 167 | os.makedirs(dir) 168 | except: 169 | raise "Can't create output dir '%s'" % dir 170 | return open(outfile, 'w') 171 | 172 | of = outfile 173 | 174 | tot_files = okay_files = failed_files = verify_failed_files = 0 175 | 176 | for file in files: 177 | infile = os.path.join(in_base, file) 178 | #print >>sys.stderr, infile 179 | 180 | if of: # outfile was given as parameter 181 | outstream = _get_outstream(outfile) 182 | elif out_base is None: 183 | outstream = sys.stdout 184 | else: 185 | outfile = os.path.join(out_base, file) + '_dis' 186 | outstream = _get_outstream(outfile) 187 | #print >>sys.stderr, outfile 188 | 189 | # try to decomyple the input file 190 | try: 191 | decompyle_file(infile, outstream, showasm, showast) 192 | tot_files += 1 193 | except KeyboardInterrupt: 194 | if outfile: 195 | outstream.close() 196 | os.remove(outfile) 197 | raise 198 | except: 199 | failed_files += 1 200 | sys.stderr.write("### Can't decompyle %s\n" % file) 201 | if outfile: 202 | outstream.close() 203 | os.rename(outfile, outfile + '_failed') 204 | raise 205 | else: # decompyle successfull 206 | if outfile: 207 | outstream.close() 208 | if do_verify: 209 | try: 210 | verify.compare_code_with_srcfile(infile, outfile) 211 | #print "+++ okay decompyling", infile, __memUsage() 212 | okay_files += 1 213 | except verify.VerifyCmpError, e: 214 | verify_failed_files += 1 215 | os.rename(outfile, outfile + '_unverified') 216 | print >>sys.stderr, "### Error Verifiying", file 217 | print >>sys.stderr, e 218 | else: 219 | okay_files += 1 220 | #print "+++ okay decompyling", infile, __memUsage() 221 | #print 'decompyled %i files: %i okay, %i failed, %i verify failed' % \ 222 | # (tot_files, okay_files, failed_files, verify_failed_files) 223 | 224 | # local variables: 225 | # tab-width: 4 226 | -------------------------------------------------------------------------------- /decompyle/decompyle/dis_20.py: -------------------------------------------------------------------------------- 1 | """Disassembler of Python byte code into mnemonics.""" 2 | 3 | import sys 4 | import string 5 | import types 6 | 7 | def dis(x=None): 8 | """Disassemble classes, methods, functions, or code. 9 | 10 | With no argument, disassemble the last traceback. 11 | 12 | """ 13 | if not x: 14 | distb() 15 | return 16 | if type(x) is types.InstanceType: 17 | x = x.__class__ 18 | if hasattr(x, '__dict__'): 19 | items = x.__dict__.items() 20 | items.sort() 21 | for name, x1 in items: 22 | if type(x1) in (types.MethodType, 23 | types.FunctionType, 24 | types.CodeType): 25 | print "Disassembly of %s:" % name 26 | try: 27 | dis(x1) 28 | except TypeError, msg: 29 | print "Sorry:", msg 30 | print 31 | else: 32 | if hasattr(x, 'im_func'): 33 | x = x.im_func 34 | if hasattr(x, 'func_code'): 35 | x = x.func_code 36 | if hasattr(x, 'co_code'): 37 | disassemble(x) 38 | else: 39 | raise TypeError, \ 40 | "don't know how to disassemble %s objects" % \ 41 | type(x).__name__ 42 | 43 | def distb(tb=None): 44 | """Disassemble a traceback (default: last traceback).""" 45 | if not tb: 46 | try: 47 | tb = sys.last_traceback 48 | except AttributeError: 49 | raise RuntimeError, "no last traceback to disassemble" 50 | while tb.tb_next: tb = tb.tb_next 51 | disassemble(tb.tb_frame.f_code, tb.tb_lasti) 52 | 53 | def disassemble(co, lasti=-1): 54 | """Disassemble a code object.""" 55 | code = co.co_code 56 | labels = findlabels(code) 57 | n = len(code) 58 | i = 0 59 | extended_arg = 0 60 | while i < n: 61 | c = code[i] 62 | op = ord(c) 63 | if op == SET_LINENO and i > 0: print # Extra blank line 64 | if i == lasti: print '-->', 65 | else: print ' ', 66 | if i in labels: print '>>', 67 | else: print ' ', 68 | print string.rjust(`i`, 4), 69 | print string.ljust(opname[op], 20), 70 | i = i+1 71 | if op >= HAVE_ARGUMENT: 72 | oparg = ord(code[i]) + ord(code[i+1])*256 + extended_arg 73 | extended_arg = 0 74 | i = i+2 75 | if op == EXTENDED_ARG: 76 | extended_arg = oparg*65536L 77 | print string.rjust(`oparg`, 5), 78 | if op in hasconst: 79 | print '(' + `co.co_consts[oparg]` + ')', 80 | elif op in hasname: 81 | print '(' + co.co_names[oparg] + ')', 82 | elif op in hasjrel: 83 | print '(to ' + `i + oparg` + ')', 84 | elif op in haslocal: 85 | print '(' + co.co_varnames[oparg] + ')', 86 | elif op in hascompare: 87 | print '(' + cmp_op[oparg] + ')', 88 | print 89 | 90 | disco = disassemble # XXX For backwards compatibility 91 | 92 | def findlabels(code): 93 | """Detect all offsets in a byte code which are jump targets. 94 | 95 | Return the list of offsets. 96 | 97 | """ 98 | labels = [] 99 | n = len(code) 100 | i = 0 101 | while i < n: 102 | c = code[i] 103 | op = ord(c) 104 | i = i+1 105 | if op >= HAVE_ARGUMENT: 106 | oparg = ord(code[i]) + ord(code[i+1])*256 107 | i = i+2 108 | label = -1 109 | if op in hasjrel: 110 | label = i+oparg 111 | elif op in hasjabs: 112 | label = oparg 113 | if label >= 0: 114 | if label not in labels: 115 | labels.append(label) 116 | return labels 117 | 118 | cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is', 119 | 'is not', 'exception match', 'BAD') 120 | 121 | hasconst = [] 122 | hasname = [] 123 | hasjrel = [] 124 | hasjabs = [] 125 | haslocal = [] 126 | hascompare = [] 127 | 128 | opname = [''] * 256 129 | for op in range(256): opname[op] = '<' + `op` + '>' 130 | 131 | def def_op(name, op): 132 | opname[op] = name 133 | 134 | def name_op(name, op): 135 | opname[op] = name 136 | hasname.append(op) 137 | 138 | def jrel_op(name, op): 139 | opname[op] = name 140 | hasjrel.append(op) 141 | 142 | def jabs_op(name, op): 143 | opname[op] = name 144 | hasjabs.append(op) 145 | 146 | # Instruction opcodes for compiled code 147 | 148 | def_op('STOP_CODE', 0) 149 | def_op('POP_TOP', 1) 150 | def_op('ROT_TWO', 2) 151 | def_op('ROT_THREE', 3) 152 | def_op('DUP_TOP', 4) 153 | def_op('ROT_FOUR', 5) 154 | 155 | def_op('UNARY_POSITIVE', 10) 156 | def_op('UNARY_NEGATIVE', 11) 157 | def_op('UNARY_NOT', 12) 158 | def_op('UNARY_CONVERT', 13) 159 | 160 | def_op('UNARY_INVERT', 15) 161 | 162 | def_op('BINARY_POWER', 19) 163 | 164 | def_op('BINARY_MULTIPLY', 20) 165 | def_op('BINARY_DIVIDE', 21) 166 | def_op('BINARY_MODULO', 22) 167 | def_op('BINARY_ADD', 23) 168 | def_op('BINARY_SUBTRACT', 24) 169 | def_op('BINARY_SUBSCR', 25) 170 | 171 | def_op('SLICE+0', 30) 172 | def_op('SLICE+1', 31) 173 | def_op('SLICE+2', 32) 174 | def_op('SLICE+3', 33) 175 | 176 | def_op('STORE_SLICE+0', 40) 177 | def_op('STORE_SLICE+1', 41) 178 | def_op('STORE_SLICE+2', 42) 179 | def_op('STORE_SLICE+3', 43) 180 | 181 | def_op('DELETE_SLICE+0', 50) 182 | def_op('DELETE_SLICE+1', 51) 183 | def_op('DELETE_SLICE+2', 52) 184 | def_op('DELETE_SLICE+3', 53) 185 | 186 | def_op('INPLACE_ADD', 55) 187 | def_op('INPLACE_SUBTRACT', 56) 188 | def_op('INPLACE_MULTIPLY', 57) 189 | def_op('INPLACE_DIVIDE', 58) 190 | def_op('INPLACE_MODULO', 59) 191 | def_op('STORE_SUBSCR', 60) 192 | def_op('DELETE_SUBSCR', 61) 193 | 194 | def_op('BINARY_LSHIFT', 62) 195 | def_op('BINARY_RSHIFT', 63) 196 | def_op('BINARY_AND', 64) 197 | def_op('BINARY_XOR', 65) 198 | def_op('BINARY_OR', 66) 199 | def_op('INPLACE_POWER', 67) 200 | 201 | def_op('PRINT_EXPR', 70) 202 | def_op('PRINT_ITEM', 71) 203 | def_op('PRINT_NEWLINE', 72) 204 | def_op('PRINT_ITEM_TO', 73) 205 | def_op('PRINT_NEWLINE_TO', 74) 206 | def_op('INPLACE_LSHIFT', 75) 207 | def_op('INPLACE_RSHIFT', 76) 208 | def_op('INPLACE_AND', 77) 209 | def_op('INPLACE_XOR', 78) 210 | def_op('INPLACE_OR', 79) 211 | def_op('BREAK_LOOP', 80) 212 | 213 | def_op('LOAD_LOCALS', 82) 214 | def_op('RETURN_VALUE', 83) 215 | def_op('IMPORT_STAR', 84) 216 | def_op('EXEC_STMT', 85) 217 | 218 | def_op('POP_BLOCK', 87) 219 | def_op('END_FINALLY', 88) 220 | def_op('BUILD_CLASS', 89) 221 | 222 | HAVE_ARGUMENT = 90 # Opcodes from here have an argument: 223 | 224 | name_op('STORE_NAME', 90) # Index in name list 225 | name_op('DELETE_NAME', 91) # "" 226 | def_op('UNPACK_SEQUENCE', 92) # Number of tuple items 227 | 228 | name_op('STORE_ATTR', 95) # Index in name list 229 | name_op('DELETE_ATTR', 96) # "" 230 | name_op('STORE_GLOBAL', 97) # "" 231 | name_op('DELETE_GLOBAL', 98) # "" 232 | def_op('DUP_TOPX', 99) # number of items to duplicate 233 | def_op('LOAD_CONST', 100) # Index in const list 234 | hasconst.append(100) 235 | name_op('LOAD_NAME', 101) # Index in name list 236 | def_op('BUILD_TUPLE', 102) # Number of tuple items 237 | def_op('BUILD_LIST', 103) # Number of list items 238 | def_op('BUILD_MAP', 104) # Always zero for now 239 | name_op('LOAD_ATTR', 105) # Index in name list 240 | def_op('COMPARE_OP', 106) # Comparison operator 241 | hascompare.append(106) 242 | name_op('IMPORT_NAME', 107) # Index in name list 243 | name_op('IMPORT_FROM', 108) # Index in name list 244 | 245 | jrel_op('JUMP_FORWARD', 110) # Number of bytes to skip 246 | jrel_op('JUMP_IF_FALSE', 111) # "" 247 | jrel_op('JUMP_IF_TRUE', 112) # "" 248 | jabs_op('JUMP_ABSOLUTE', 113) # Target byte offset from beginning of code 249 | jrel_op('FOR_LOOP', 114) # Number of bytes to skip 250 | 251 | name_op('LOAD_GLOBAL', 116) # Index in name list 252 | 253 | jrel_op('SETUP_LOOP', 120) # Distance to target address 254 | jrel_op('SETUP_EXCEPT', 121) # "" 255 | jrel_op('SETUP_FINALLY', 122) # "" 256 | 257 | def_op('LOAD_FAST', 124) # Local variable number 258 | haslocal.append(124) 259 | def_op('STORE_FAST', 125) # Local variable number 260 | haslocal.append(125) 261 | def_op('DELETE_FAST', 126) # Local variable number 262 | haslocal.append(126) 263 | 264 | def_op('SET_LINENO', 127) # Current line number 265 | SET_LINENO = 127 266 | 267 | def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3) 268 | def_op('CALL_FUNCTION', 131) # #args + (#kwargs << 8) 269 | def_op('MAKE_FUNCTION', 132) # Number of args with default values 270 | def_op('BUILD_SLICE', 133) # Number of items 271 | 272 | def_op('CALL_FUNCTION_VAR', 140) # #args + (#kwargs << 8) 273 | def_op('CALL_FUNCTION_KW', 141) # #args + (#kwargs << 8) 274 | def_op('CALL_FUNCTION_VAR_KW', 142) # #args + (#kwargs << 8) 275 | 276 | def_op('EXTENDED_ARG', 143) 277 | EXTENDED_ARG = 143 278 | 279 | def _test(): 280 | """Simple test program to disassemble a file.""" 281 | if sys.argv[1:]: 282 | if sys.argv[2:]: 283 | sys.stderr.write("usage: python dis.py [-|file]\n") 284 | sys.exit(2) 285 | fn = sys.argv[1] 286 | if not fn or fn == "-": 287 | fn = None 288 | else: 289 | fn = None 290 | if not fn: 291 | f = sys.stdin 292 | else: 293 | f = open(fn) 294 | source = f.read() 295 | if fn: 296 | f.close() 297 | else: 298 | fn = "" 299 | code = compile(source, fn, "exec") 300 | dis(code) 301 | 302 | if __name__ == "__main__": 303 | _test() 304 | -------------------------------------------------------------------------------- /decompyle/decompyle/dis_21.py: -------------------------------------------------------------------------------- 1 | """Disassembler of Python byte code into mnemonics.""" 2 | 3 | import sys 4 | import types 5 | 6 | __all__ = ["dis","disassemble","distb","disco","opname","cmp_op", 7 | "hasconst","hasname","hasjrel","hasjabs","haslocal", 8 | "hascompare", "hasfree"] 9 | 10 | def dis(x=None): 11 | """Disassemble classes, methods, functions, or code. 12 | 13 | With no argument, disassemble the last traceback. 14 | 15 | """ 16 | if not x: 17 | distb() 18 | return 19 | if type(x) is types.InstanceType: 20 | x = x.__class__ 21 | if hasattr(x, 'im_func'): 22 | x = x.im_func 23 | if hasattr(x, 'func_code'): 24 | x = x.func_code 25 | if hasattr(x, '__dict__'): 26 | items = x.__dict__.items() 27 | items.sort() 28 | for name, x1 in items: 29 | if type(x1) in (types.MethodType, 30 | types.FunctionType, 31 | types.CodeType): 32 | print "Disassembly of %s:" % name 33 | try: 34 | dis(x1) 35 | except TypeError, msg: 36 | print "Sorry:", msg 37 | print 38 | elif hasattr(x, 'co_code'): 39 | disassemble(x) 40 | else: 41 | raise TypeError, \ 42 | "don't know how to disassemble %s objects" % \ 43 | type(x).__name__ 44 | 45 | def distb(tb=None): 46 | """Disassemble a traceback (default: last traceback).""" 47 | if not tb: 48 | try: 49 | tb = sys.last_traceback 50 | except AttributeError: 51 | raise RuntimeError, "no last traceback to disassemble" 52 | while tb.tb_next: tb = tb.tb_next 53 | disassemble(tb.tb_frame.f_code, tb.tb_lasti) 54 | 55 | def disassemble(co, lasti=-1): 56 | """Disassemble a code object.""" 57 | code = co.co_code 58 | labels = findlabels(code) 59 | n = len(code) 60 | i = 0 61 | extended_arg = 0 62 | free = None 63 | while i < n: 64 | c = code[i] 65 | op = ord(c) 66 | if op == SET_LINENO and i > 0: print # Extra blank line 67 | if i == lasti: print '-->', 68 | else: print ' ', 69 | if i in labels: print '>>', 70 | else: print ' ', 71 | print `i`.rjust(4), 72 | print opname[op].ljust(20), 73 | i = i+1 74 | if op >= HAVE_ARGUMENT: 75 | oparg = ord(code[i]) + ord(code[i+1])*256 + extended_arg 76 | extended_arg = 0 77 | i = i+2 78 | if op == EXTENDED_ARG: 79 | extended_arg = oparg*65536L 80 | print `oparg`.rjust(5), 81 | if op in hasconst: 82 | print '(' + `co.co_consts[oparg]` + ')', 83 | elif op in hasname: 84 | print '(' + co.co_names[oparg] + ')', 85 | elif op in hasjrel: 86 | print '(to ' + `i + oparg` + ')', 87 | elif op in haslocal: 88 | print '(' + co.co_varnames[oparg] + ')', 89 | elif op in hascompare: 90 | print '(' + cmp_op[oparg] + ')', 91 | elif op in hasfree: 92 | if free is None: 93 | free = co.co_cellvars + co.co_freevars 94 | print '(' + free[oparg] + ')', 95 | print 96 | 97 | disco = disassemble # XXX For backwards compatibility 98 | 99 | def findlabels(code): 100 | """Detect all offsets in a byte code which are jump targets. 101 | 102 | Return the list of offsets. 103 | 104 | """ 105 | labels = [] 106 | n = len(code) 107 | i = 0 108 | while i < n: 109 | c = code[i] 110 | op = ord(c) 111 | i = i+1 112 | if op >= HAVE_ARGUMENT: 113 | oparg = ord(code[i]) + ord(code[i+1])*256 114 | i = i+2 115 | label = -1 116 | if op in hasjrel: 117 | label = i+oparg 118 | elif op in hasjabs: 119 | label = oparg 120 | if label >= 0: 121 | if label not in labels: 122 | labels.append(label) 123 | return labels 124 | 125 | cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is', 126 | 'is not', 'exception match', 'BAD') 127 | 128 | hasconst = [] 129 | hasname = [] 130 | hasjrel = [] 131 | hasjabs = [] 132 | haslocal = [] 133 | hascompare = [] 134 | hasfree = [] 135 | 136 | opname = [''] * 256 137 | for op in range(256): opname[op] = '<' + `op` + '>' 138 | 139 | def def_op(name, op): 140 | opname[op] = name 141 | 142 | def name_op(name, op): 143 | opname[op] = name 144 | hasname.append(op) 145 | 146 | def jrel_op(name, op): 147 | opname[op] = name 148 | hasjrel.append(op) 149 | 150 | def jabs_op(name, op): 151 | opname[op] = name 152 | hasjabs.append(op) 153 | 154 | # Instruction opcodes for compiled code 155 | 156 | def_op('STOP_CODE', 0) 157 | def_op('POP_TOP', 1) 158 | def_op('ROT_TWO', 2) 159 | def_op('ROT_THREE', 3) 160 | def_op('DUP_TOP', 4) 161 | def_op('ROT_FOUR', 5) 162 | 163 | def_op('UNARY_POSITIVE', 10) 164 | def_op('UNARY_NEGATIVE', 11) 165 | def_op('UNARY_NOT', 12) 166 | def_op('UNARY_CONVERT', 13) 167 | 168 | def_op('UNARY_INVERT', 15) 169 | 170 | def_op('BINARY_POWER', 19) 171 | 172 | def_op('BINARY_MULTIPLY', 20) 173 | def_op('BINARY_DIVIDE', 21) 174 | def_op('BINARY_MODULO', 22) 175 | def_op('BINARY_ADD', 23) 176 | def_op('BINARY_SUBTRACT', 24) 177 | def_op('BINARY_SUBSCR', 25) 178 | 179 | def_op('SLICE+0', 30) 180 | def_op('SLICE+1', 31) 181 | def_op('SLICE+2', 32) 182 | def_op('SLICE+3', 33) 183 | 184 | def_op('STORE_SLICE+0', 40) 185 | def_op('STORE_SLICE+1', 41) 186 | def_op('STORE_SLICE+2', 42) 187 | def_op('STORE_SLICE+3', 43) 188 | 189 | def_op('DELETE_SLICE+0', 50) 190 | def_op('DELETE_SLICE+1', 51) 191 | def_op('DELETE_SLICE+2', 52) 192 | def_op('DELETE_SLICE+3', 53) 193 | 194 | def_op('INPLACE_ADD', 55) 195 | def_op('INPLACE_SUBTRACT', 56) 196 | def_op('INPLACE_MULTIPLY', 57) 197 | def_op('INPLACE_DIVIDE', 58) 198 | def_op('INPLACE_MODULO', 59) 199 | def_op('STORE_SUBSCR', 60) 200 | def_op('DELETE_SUBSCR', 61) 201 | 202 | def_op('BINARY_LSHIFT', 62) 203 | def_op('BINARY_RSHIFT', 63) 204 | def_op('BINARY_AND', 64) 205 | def_op('BINARY_XOR', 65) 206 | def_op('BINARY_OR', 66) 207 | def_op('INPLACE_POWER', 67) 208 | 209 | def_op('PRINT_EXPR', 70) 210 | def_op('PRINT_ITEM', 71) 211 | def_op('PRINT_NEWLINE', 72) 212 | def_op('PRINT_ITEM_TO', 73) 213 | def_op('PRINT_NEWLINE_TO', 74) 214 | def_op('INPLACE_LSHIFT', 75) 215 | def_op('INPLACE_RSHIFT', 76) 216 | def_op('INPLACE_AND', 77) 217 | def_op('INPLACE_XOR', 78) 218 | def_op('INPLACE_OR', 79) 219 | def_op('BREAK_LOOP', 80) 220 | 221 | def_op('LOAD_LOCALS', 82) 222 | def_op('RETURN_VALUE', 83) 223 | def_op('IMPORT_STAR', 84) 224 | def_op('EXEC_STMT', 85) 225 | 226 | def_op('POP_BLOCK', 87) 227 | def_op('END_FINALLY', 88) 228 | def_op('BUILD_CLASS', 89) 229 | 230 | HAVE_ARGUMENT = 90 # Opcodes from here have an argument: 231 | 232 | name_op('STORE_NAME', 90) # Index in name list 233 | name_op('DELETE_NAME', 91) # "" 234 | def_op('UNPACK_SEQUENCE', 92) # Number of tuple items 235 | 236 | name_op('STORE_ATTR', 95) # Index in name list 237 | name_op('DELETE_ATTR', 96) # "" 238 | name_op('STORE_GLOBAL', 97) # "" 239 | name_op('DELETE_GLOBAL', 98) # "" 240 | def_op('DUP_TOPX', 99) # number of items to duplicate 241 | def_op('LOAD_CONST', 100) # Index in const list 242 | hasconst.append(100) 243 | name_op('LOAD_NAME', 101) # Index in name list 244 | def_op('BUILD_TUPLE', 102) # Number of tuple items 245 | def_op('BUILD_LIST', 103) # Number of list items 246 | def_op('BUILD_MAP', 104) # Always zero for now 247 | name_op('LOAD_ATTR', 105) # Index in name list 248 | def_op('COMPARE_OP', 106) # Comparison operator 249 | hascompare.append(106) 250 | name_op('IMPORT_NAME', 107) # Index in name list 251 | name_op('IMPORT_FROM', 108) # Index in name list 252 | 253 | jrel_op('JUMP_FORWARD', 110) # Number of bytes to skip 254 | jrel_op('JUMP_IF_FALSE', 111) # "" 255 | jrel_op('JUMP_IF_TRUE', 112) # "" 256 | jabs_op('JUMP_ABSOLUTE', 113) # Target byte offset from beginning of code 257 | jrel_op('FOR_LOOP', 114) # Number of bytes to skip 258 | 259 | name_op('LOAD_GLOBAL', 116) # Index in name list 260 | 261 | jabs_op('CONTINUE_LOOP', 119) # Target address 262 | jrel_op('SETUP_LOOP', 120) # Distance to target address 263 | jrel_op('SETUP_EXCEPT', 121) # "" 264 | jrel_op('SETUP_FINALLY', 122) # "" 265 | 266 | def_op('LOAD_FAST', 124) # Local variable number 267 | haslocal.append(124) 268 | def_op('STORE_FAST', 125) # Local variable number 269 | haslocal.append(125) 270 | def_op('DELETE_FAST', 126) # Local variable number 271 | haslocal.append(126) 272 | 273 | def_op('SET_LINENO', 127) # Current line number 274 | SET_LINENO = 127 275 | 276 | def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3) 277 | def_op('CALL_FUNCTION', 131) # #args + (#kwargs << 8) 278 | def_op('MAKE_FUNCTION', 132) # Number of args with default values 279 | def_op('BUILD_SLICE', 133) # Number of items 280 | 281 | def_op('MAKE_CLOSURE', 134) 282 | def_op('LOAD_CLOSURE', 135) 283 | hasfree.append(135) 284 | def_op('LOAD_DEREF', 136) 285 | hasfree.append(136) 286 | def_op('STORE_DEREF', 137) 287 | hasfree.append(137) 288 | 289 | def_op('CALL_FUNCTION_VAR', 140) # #args + (#kwargs << 8) 290 | def_op('CALL_FUNCTION_KW', 141) # #args + (#kwargs << 8) 291 | def_op('CALL_FUNCTION_VAR_KW', 142) # #args + (#kwargs << 8) 292 | 293 | def_op('EXTENDED_ARG', 143) 294 | EXTENDED_ARG = 143 295 | 296 | def _test(): 297 | """Simple test program to disassemble a file.""" 298 | if sys.argv[1:]: 299 | if sys.argv[2:]: 300 | sys.stderr.write("usage: python dis.py [-|file]\n") 301 | sys.exit(2) 302 | fn = sys.argv[1] 303 | if not fn or fn == "-": 304 | fn = None 305 | else: 306 | fn = None 307 | if not fn: 308 | f = sys.stdin 309 | else: 310 | f = open(fn) 311 | source = f.read() 312 | if fn: 313 | f.close() 314 | else: 315 | fn = "" 316 | code = compile(source, fn, "exec") 317 | dis(code) 318 | 319 | if __name__ == "__main__": 320 | _test() 321 | -------------------------------------------------------------------------------- /decompyle/decompyle/dis_22.py: -------------------------------------------------------------------------------- 1 | """Disassembler of Python byte code into mnemonics.""" 2 | 3 | import sys 4 | import types 5 | 6 | __all__ = ["dis","disassemble","distb","disco","opname","cmp_op", 7 | "hasconst","hasname","hasjrel","hasjabs","haslocal", 8 | "hascompare", "hasfree"] 9 | 10 | def dis(x=None): 11 | """Disassemble classes, methods, functions, or code. 12 | 13 | With no argument, disassemble the last traceback. 14 | 15 | """ 16 | if not x: 17 | distb() 18 | return 19 | if type(x) is types.InstanceType: 20 | x = x.__class__ 21 | if hasattr(x, 'im_func'): 22 | x = x.im_func 23 | if hasattr(x, 'func_code'): 24 | x = x.func_code 25 | if hasattr(x, '__dict__'): 26 | items = x.__dict__.items() 27 | items.sort() 28 | for name, x1 in items: 29 | if type(x1) in (types.MethodType, 30 | types.FunctionType, 31 | types.CodeType): 32 | print "Disassembly of %s:" % name 33 | try: 34 | dis(x1) 35 | except TypeError, msg: 36 | print "Sorry:", msg 37 | print 38 | elif hasattr(x, 'co_code'): 39 | disassemble(x) 40 | else: 41 | raise TypeError, \ 42 | "don't know how to disassemble %s objects" % \ 43 | type(x).__name__ 44 | 45 | def distb(tb=None): 46 | """Disassemble a traceback (default: last traceback).""" 47 | if not tb: 48 | try: 49 | tb = sys.last_traceback 50 | except AttributeError: 51 | raise RuntimeError, "no last traceback to disassemble" 52 | while tb.tb_next: tb = tb.tb_next 53 | disassemble(tb.tb_frame.f_code, tb.tb_lasti) 54 | 55 | def disassemble(co, lasti=-1): 56 | """Disassemble a code object.""" 57 | code = co.co_code 58 | labels = findlabels(code) 59 | n = len(code) 60 | i = 0 61 | extended_arg = 0 62 | free = None 63 | while i < n: 64 | c = code[i] 65 | op = ord(c) 66 | if op == SET_LINENO and i > 0: print # Extra blank line 67 | if i == lasti: print '-->', 68 | else: print ' ', 69 | if i in labels: print '>>', 70 | else: print ' ', 71 | print `i`.rjust(4), 72 | print opname[op].ljust(20), 73 | i = i+1 74 | if op >= HAVE_ARGUMENT: 75 | oparg = ord(code[i]) + ord(code[i+1])*256 + extended_arg 76 | extended_arg = 0 77 | i = i+2 78 | if op == EXTENDED_ARG: 79 | extended_arg = oparg*65536L 80 | print `oparg`.rjust(5), 81 | if op in hasconst: 82 | print '(' + `co.co_consts[oparg]` + ')', 83 | elif op in hasname: 84 | print '(' + co.co_names[oparg] + ')', 85 | elif op in hasjrel: 86 | print '(to ' + `i + oparg` + ')', 87 | elif op in haslocal: 88 | print '(' + co.co_varnames[oparg] + ')', 89 | elif op in hascompare: 90 | print '(' + cmp_op[oparg] + ')', 91 | elif op in hasfree: 92 | if free is None: 93 | free = co.co_cellvars + co.co_freevars 94 | print '(' + free[oparg] + ')', 95 | print 96 | 97 | disco = disassemble # XXX For backwards compatibility 98 | 99 | def findlabels(code): 100 | """Detect all offsets in a byte code which are jump targets. 101 | 102 | Return the list of offsets. 103 | 104 | """ 105 | labels = [] 106 | n = len(code) 107 | i = 0 108 | while i < n: 109 | c = code[i] 110 | op = ord(c) 111 | i = i+1 112 | if op >= HAVE_ARGUMENT: 113 | oparg = ord(code[i]) + ord(code[i+1])*256 114 | i = i+2 115 | label = -1 116 | if op in hasjrel: 117 | label = i+oparg 118 | elif op in hasjabs: 119 | label = oparg 120 | if label >= 0: 121 | if label not in labels: 122 | labels.append(label) 123 | return labels 124 | 125 | cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is', 126 | 'is not', 'exception match', 'BAD') 127 | 128 | hasconst = [] 129 | hasname = [] 130 | hasjrel = [] 131 | hasjabs = [] 132 | haslocal = [] 133 | hascompare = [] 134 | hasfree = [] 135 | 136 | opname = [''] * 256 137 | for op in range(256): opname[op] = '<' + `op` + '>' 138 | 139 | def def_op(name, op): 140 | opname[op] = name 141 | 142 | def name_op(name, op): 143 | opname[op] = name 144 | hasname.append(op) 145 | 146 | def jrel_op(name, op): 147 | opname[op] = name 148 | hasjrel.append(op) 149 | 150 | def jabs_op(name, op): 151 | opname[op] = name 152 | hasjabs.append(op) 153 | 154 | # Instruction opcodes for compiled code 155 | 156 | def_op('STOP_CODE', 0) 157 | def_op('POP_TOP', 1) 158 | def_op('ROT_TWO', 2) 159 | def_op('ROT_THREE', 3) 160 | def_op('DUP_TOP', 4) 161 | def_op('ROT_FOUR', 5) 162 | 163 | def_op('UNARY_POSITIVE', 10) 164 | def_op('UNARY_NEGATIVE', 11) 165 | def_op('UNARY_NOT', 12) 166 | def_op('UNARY_CONVERT', 13) 167 | 168 | def_op('UNARY_INVERT', 15) 169 | 170 | def_op('BINARY_POWER', 19) 171 | 172 | def_op('BINARY_MULTIPLY', 20) 173 | def_op('BINARY_DIVIDE', 21) 174 | def_op('BINARY_MODULO', 22) 175 | def_op('BINARY_ADD', 23) 176 | def_op('BINARY_SUBTRACT', 24) 177 | def_op('BINARY_SUBSCR', 25) 178 | def_op('BINARY_FLOOR_DIVIDE', 26) 179 | def_op('BINARY_TRUE_DIVIDE', 27) 180 | def_op('INPLACE_FLOOR_DIVIDE', 28) 181 | def_op('INPLACE_TRUE_DIVIDE', 29) 182 | 183 | def_op('SLICE+0', 30) 184 | def_op('SLICE+1', 31) 185 | def_op('SLICE+2', 32) 186 | def_op('SLICE+3', 33) 187 | 188 | def_op('STORE_SLICE+0', 40) 189 | def_op('STORE_SLICE+1', 41) 190 | def_op('STORE_SLICE+2', 42) 191 | def_op('STORE_SLICE+3', 43) 192 | 193 | def_op('DELETE_SLICE+0', 50) 194 | def_op('DELETE_SLICE+1', 51) 195 | def_op('DELETE_SLICE+2', 52) 196 | def_op('DELETE_SLICE+3', 53) 197 | 198 | def_op('INPLACE_ADD', 55) 199 | def_op('INPLACE_SUBTRACT', 56) 200 | def_op('INPLACE_MULTIPLY', 57) 201 | def_op('INPLACE_DIVIDE', 58) 202 | def_op('INPLACE_MODULO', 59) 203 | def_op('STORE_SUBSCR', 60) 204 | def_op('DELETE_SUBSCR', 61) 205 | 206 | def_op('BINARY_LSHIFT', 62) 207 | def_op('BINARY_RSHIFT', 63) 208 | def_op('BINARY_AND', 64) 209 | def_op('BINARY_XOR', 65) 210 | def_op('BINARY_OR', 66) 211 | def_op('INPLACE_POWER', 67) 212 | def_op('GET_ITER', 68) 213 | 214 | def_op('PRINT_EXPR', 70) 215 | def_op('PRINT_ITEM', 71) 216 | def_op('PRINT_NEWLINE', 72) 217 | def_op('PRINT_ITEM_TO', 73) 218 | def_op('PRINT_NEWLINE_TO', 74) 219 | def_op('INPLACE_LSHIFT', 75) 220 | def_op('INPLACE_RSHIFT', 76) 221 | def_op('INPLACE_AND', 77) 222 | def_op('INPLACE_XOR', 78) 223 | def_op('INPLACE_OR', 79) 224 | def_op('BREAK_LOOP', 80) 225 | 226 | def_op('LOAD_LOCALS', 82) 227 | def_op('RETURN_VALUE', 83) 228 | def_op('IMPORT_STAR', 84) 229 | def_op('EXEC_STMT', 85) 230 | def_op('YIELD_STMT', 86) 231 | 232 | def_op('POP_BLOCK', 87) 233 | def_op('END_FINALLY', 88) 234 | def_op('BUILD_CLASS', 89) 235 | 236 | HAVE_ARGUMENT = 90 # Opcodes from here have an argument: 237 | 238 | name_op('STORE_NAME', 90) # Index in name list 239 | name_op('DELETE_NAME', 91) # "" 240 | def_op('UNPACK_SEQUENCE', 92) # Number of tuple items 241 | jrel_op('FOR_ITER', 93) 242 | 243 | name_op('STORE_ATTR', 95) # Index in name list 244 | name_op('DELETE_ATTR', 96) # "" 245 | name_op('STORE_GLOBAL', 97) # "" 246 | name_op('DELETE_GLOBAL', 98) # "" 247 | def_op('DUP_TOPX', 99) # number of items to duplicate 248 | def_op('LOAD_CONST', 100) # Index in const list 249 | hasconst.append(100) 250 | name_op('LOAD_NAME', 101) # Index in name list 251 | def_op('BUILD_TUPLE', 102) # Number of tuple items 252 | def_op('BUILD_LIST', 103) # Number of list items 253 | def_op('BUILD_MAP', 104) # Always zero for now 254 | name_op('LOAD_ATTR', 105) # Index in name list 255 | def_op('COMPARE_OP', 106) # Comparison operator 256 | hascompare.append(106) 257 | name_op('IMPORT_NAME', 107) # Index in name list 258 | name_op('IMPORT_FROM', 108) # Index in name list 259 | 260 | jrel_op('JUMP_FORWARD', 110) # Number of bytes to skip 261 | jrel_op('JUMP_IF_FALSE', 111) # "" 262 | jrel_op('JUMP_IF_TRUE', 112) # "" 263 | jabs_op('JUMP_ABSOLUTE', 113) # Target byte offset from beginning of code 264 | jrel_op('FOR_LOOP', 114) # Number of bytes to skip 265 | 266 | name_op('LOAD_GLOBAL', 116) # Index in name list 267 | 268 | jabs_op('CONTINUE_LOOP', 119) # Target address 269 | jrel_op('SETUP_LOOP', 120) # Distance to target address 270 | jrel_op('SETUP_EXCEPT', 121) # "" 271 | jrel_op('SETUP_FINALLY', 122) # "" 272 | 273 | def_op('LOAD_FAST', 124) # Local variable number 274 | haslocal.append(124) 275 | def_op('STORE_FAST', 125) # Local variable number 276 | haslocal.append(125) 277 | def_op('DELETE_FAST', 126) # Local variable number 278 | haslocal.append(126) 279 | 280 | def_op('SET_LINENO', 127) # Current line number 281 | SET_LINENO = 127 282 | 283 | def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3) 284 | def_op('CALL_FUNCTION', 131) # #args + (#kwargs << 8) 285 | def_op('MAKE_FUNCTION', 132) # Number of args with default values 286 | def_op('BUILD_SLICE', 133) # Number of items 287 | 288 | def_op('MAKE_CLOSURE', 134) 289 | def_op('LOAD_CLOSURE', 135) 290 | hasfree.append(135) 291 | def_op('LOAD_DEREF', 136) 292 | hasfree.append(136) 293 | def_op('STORE_DEREF', 137) 294 | hasfree.append(137) 295 | 296 | def_op('CALL_FUNCTION_VAR', 140) # #args + (#kwargs << 8) 297 | def_op('CALL_FUNCTION_KW', 141) # #args + (#kwargs << 8) 298 | def_op('CALL_FUNCTION_VAR_KW', 142) # #args + (#kwargs << 8) 299 | 300 | def_op('EXTENDED_ARG', 143) 301 | EXTENDED_ARG = 143 302 | 303 | def _test(): 304 | """Simple test program to disassemble a file.""" 305 | if sys.argv[1:]: 306 | if sys.argv[2:]: 307 | sys.stderr.write("usage: python dis.py [-|file]\n") 308 | sys.exit(2) 309 | fn = sys.argv[1] 310 | if not fn or fn == "-": 311 | fn = None 312 | else: 313 | fn = None 314 | if not fn: 315 | f = sys.stdin 316 | else: 317 | f = open(fn) 318 | source = f.read() 319 | if fn: 320 | f.close() 321 | else: 322 | fn = "" 323 | code = compile(source, fn, "exec") 324 | dis(code) 325 | 326 | if __name__ == "__main__": 327 | _test() 328 | -------------------------------------------------------------------------------- /lib/unholy/pyasm.rb: -------------------------------------------------------------------------------- 1 | class Pyasm 2 | CO_NEWLOCALS = 0x02 3 | CO_NOFREE = 0x40 4 | 5 | VM_CALL_FCALL_BIT = 0x08 6 | 7 | OPS = { 8 | :== => 2 9 | } 10 | 11 | attr_accessor :argc, :stacksize, :flags, :consts, :bytecode, 12 | :filename, :lineno, :name, :symbols, :stacknow, :varsyms, :jumps, :labels, :lines 13 | def initialize(fname, type = nil, name = "", lineno = 0, argc = 0) 14 | @argc, @stacksize, @flags, @filename, @lineno, @name, @stack, @nopop, @type = 15 | argc, 1, CO_NOFREE, fname, lineno, name, [], 0, type 16 | @flags |= CO_NEWLOCALS if [:class, :method].include?(type) 17 | @consts = [-1, nil] 18 | @symbols = [:Kernel] 19 | @bytecode, @varsyms, @labels, @lines, @jumps = [], [], {}, [], {} 20 | end 21 | 22 | def add_const(obj) 23 | @consts << obj unless @consts.include? obj 24 | @consts.index(obj) 25 | end 26 | def add_sym(name) 27 | @symbols << name unless @symbols.include? name 28 | @symbols.index(name) 29 | end 30 | def add_varsym(name) 31 | @varsyms << name unless @varsyms.include? name 32 | @varsyms.index(name) 33 | end 34 | def stack_push obj, b 35 | @stack << [obj, b] 36 | @stacksize = @stack.length if @stack.length > @stacksize 37 | b 38 | end 39 | def dump_stack 40 | @stack.clear 41 | end 42 | def bc *bytes 43 | @bytecode << bytes 44 | bytes 45 | end 46 | def mark_jump n, bc 47 | @jumps[n] ||= [] 48 | @jumps[n] << bc 49 | end 50 | 51 | def pop_top; bc 0x01; dump_stack end 52 | def infix i 53 | add = bc i 54 | @stack.pop 55 | add 56 | end 57 | def binary_add; infix 0x17 end 58 | def binary_subscr; infix 0x19 end 59 | def inplace_add; infix 0x37 end 60 | def print_item; dump_stack; bc 0x47 end 61 | def print_newline; dump_stack; bc 0x48 end 62 | def load_locals; bc 0x52 end 63 | def ret_val; bc 0x53 end 64 | def build_class; bc 0x59 end 65 | def store_name(name) 66 | dump_stack 67 | bc 0x5a, add_sym(name), 0x0 68 | end 69 | def store_attr(name) 70 | dump_stack 71 | bc 0x5f, add_sym(name), 0x0 72 | end 73 | def load_const(obj) 74 | stack_push obj, bc(0x64, add_const(obj), 0x0) 75 | end 76 | def load_name(name) 77 | stack_push name, bc(0x65, add_sym(name), 0x0) 78 | end 79 | def build_tuple(n) 80 | @stack.slice! -(n-1), (n-1) 81 | bc(0x66, n, 0x0) 82 | end 83 | def build_list(n) 84 | @stack.slice! -(n-1), (n-1) if n > 0 85 | bc(0x67, n, 0x0) 86 | end 87 | def load_attr(name) 88 | stack_push name, bc(0x69, add_sym(name), 0x0) 89 | end 90 | def compare_op(op) 91 | bc 0x6a, OPS[op], 0x0 92 | end 93 | def import_name(name) 94 | bc 0x6b, add_sym(name), 0x0 95 | end 96 | def import_from(name) 97 | bc 0x6c, add_sym(name), 0x0 98 | end 99 | def jump(n) 100 | mark_jump n, bc(0x6e, n, 0x0) 101 | end 102 | def jump_if_false(n) 103 | mark_jump n, bc(0x6f, n, 0x0) 104 | end 105 | def load_fast(n) 106 | stack_push n, bc(0x7c, n, 0x0) 107 | end 108 | def store_fast(n) 109 | dump_stack 110 | bc 0x7d, n, 0x0 111 | end 112 | def call_func(arity) 113 | (arity + 2).times { @stack.pop } 114 | stack_push Object.new, bc(0x83, arity, 0x0) 115 | end 116 | def make_func(arity) 117 | bc 0x84, arity, 0x0 118 | end 119 | def line n 120 | @lines << [n, @bytecode.flatten.length] 121 | end 122 | def prep_send 123 | @nopop -= 1 if @nopop > 0 124 | end 125 | def label l 126 | @labels[l] = @bytecode.flatten.length 127 | end 128 | 129 | def import_split(mod) 130 | mod = mod.to_s 131 | return [mod[/^(.+?)(\.|$)/, 1].intern, mod.intern] 132 | end 133 | 134 | def getinlinecache ic, dst 135 | end 136 | def setinlinecache dst 137 | end 138 | def getlocal id 139 | load_fast id - 2 140 | end 141 | def setlocal id 142 | store_fast id - 2 143 | end 144 | def getdynamic id, lvl 145 | load_name @varsyms[id - 1] 146 | end 147 | def getconstant sym 148 | load_name(sym) 149 | end 150 | def print_obj 151 | print_item 152 | print_newline 153 | end 154 | def import(mod, inner = nil) 155 | load_const(-1) 156 | 157 | mod_in, mod = import_split(mod) 158 | if inner 159 | inner_in, inner = import_split(inner) 160 | load_const(tuple(inner)) 161 | mod_in = inner_in 162 | else 163 | load_const(nil) 164 | end 165 | 166 | import_name(mod) 167 | import_from(inner) if inner 168 | store_name(mod_in) 169 | pop_top if inner 170 | end 171 | def from(inner, mod) 172 | import(inner, mod) 173 | end 174 | def kernel_send(meth, *args) 175 | load_name(:Kernel) 176 | load_attr(meth) 177 | args.each do |obj| 178 | load_const(obj) 179 | end 180 | call_func(args.length) 181 | end 182 | 183 | def leave 184 | if @type == :class 185 | load_locals 186 | else 187 | load_const(nil) if @stack.empty? 188 | end 189 | ret_val 190 | end 191 | def duparray ary 192 | ary.each do |x| 193 | load_const x 194 | end 195 | build_list ary.length 196 | end 197 | def newarray size 198 | build_list size 199 | end 200 | def opt_aref 201 | binary_subscr 202 | end 203 | def opt_ltlt 204 | build_list 1 205 | inplace_add 206 | end 207 | def opt_plus 208 | binary_add 209 | end 210 | def putnil 211 | load_const(nil) 212 | end 213 | def putstring(str) 214 | str = @filename if str == "" 215 | load_const(str) 216 | end 217 | def putobject(obj) 218 | load_const(obj) 219 | end 220 | def message(meth, op_argc, blockiseq, op_flag, ic) 221 | # args = @stack.slice! -op_argc, op_argc 222 | # bytes = @bytecode.slice! -op_argc, op_argc 223 | 224 | argc = op_argc + 1 225 | receiver, recbytes = @stack[-argc] 226 | args = @stack[-op_argc, op_argc].map { |o,_| o } 227 | idx = @bytecode.index { |x| x.object_id == recbytes.object_id } 228 | bytes = [] 229 | 230 | if idx 231 | bytes = @bytecode.slice! idx..-1 232 | 233 | unless receiver 234 | unpop 235 | case meth 236 | when :print 237 | @bytecode += bytes 238 | return print_obj 239 | when :import 240 | return import(*args) 241 | when :from 242 | return from(*args) 243 | when :tuple 244 | return load_const(tuple(*args)) 245 | else 246 | load_name(:Kernel) 247 | bytes.shift 248 | end 249 | else 250 | @bytecode << bytes.shift 251 | end 252 | elsif op_argc > 0 253 | idx = @bytecode.index { |x| x.object_id == @stack[-op_argc][1].object_id } 254 | bytes = @bytecode.slice! idx..-1 255 | end 256 | 257 | if meth != :new 258 | load_attr(meth) 259 | end 260 | @bytecode += bytes 261 | 262 | if blockiseq 263 | blk = Pyasm.new(@filename, :block, "", @lines.last[0]) 264 | blk.load_iseq blockiseq 265 | load_const(blk) 266 | op_argc += 1 267 | end 268 | 269 | call_func(op_argc) 270 | end 271 | def unpop 272 | @nopop = 2 273 | end 274 | def pop 275 | pop_top unless @nopop > 0 or @stack.empty? 276 | end 277 | def opt_eq arg 278 | compare_op :== 279 | end 280 | def getglobal sym 281 | case sym when :$0 282 | load_const(@filename) 283 | end 284 | end 285 | def defineclass klass, iseq, is_singleton 286 | define :class, klass, iseq, is_singleton 287 | end 288 | def definemethod meth, iseq, is_singleton 289 | define :method, meth, iseq, is_singleton 290 | end 291 | def define type, id, iseq, is_singleton 292 | c = (type == :class) ? -2 : -1 293 | receiver, recbytes = @stack[c] 294 | idx = @bytecode.index { |x| x.object_id == recbytes.object_id } 295 | bytes = @bytecode.slice! idx..-1 296 | bytes.shift unless receiver 297 | 298 | argc = iseq[4][:arg_size] 299 | argc += 1 if type == :method # python's self 300 | asm = Pyasm.new(@filename, type, id.to_s, @lines.last[0], argc) 301 | if type == :class 302 | asm.load_name(:__name__) 303 | asm.store_name(:__module__) 304 | end 305 | asm.load_iseq iseq 306 | if type == :class 307 | load_const(id.to_s) 308 | if bytes.empty? or @consts[bytes[0][1]].nil? 309 | load_const(tuple()) 310 | else 311 | @bytecode += bytes 312 | build_tuple(1) 313 | end 314 | end 315 | load_const(asm) 316 | make_func(0) 317 | if type == :class 318 | call_func(0) 319 | build_class 320 | else 321 | @bytecode += bytes 322 | end 323 | store_name(id) 324 | 325 | # this is a bit redundant, but decompyle requires it 326 | if type == :method 327 | if @type == nil 328 | unless receiver 329 | load_name(id) 330 | load_name(:Kernel) 331 | store_attr(id) 332 | end 333 | elsif @type == :class and id == :initialize 334 | load_name(id) 335 | store_name(:__init__) 336 | end 337 | end 338 | end 339 | def branchunless label 340 | jump_if_false label 341 | end 342 | 343 | def load_iseq iseq 344 | iseq = iseq.to_a 345 | @varsyms += iseq[8].reverse 346 | 347 | iseq[11].each do |inst| 348 | case inst 349 | when Integer # line no 350 | line inst 351 | when Symbol 352 | label inst 353 | when Array 354 | inst[0] = :message if inst[0] == :send 355 | self.prep_send 356 | self.send *inst 357 | end 358 | end 359 | end 360 | def eval src 361 | begin 362 | iseq = VM::InstructionSequence.compile(src) 363 | load_iseq iseq 364 | rescue NameError => e 365 | puts "*** Are you sure you're running Ruby 1.9?" 366 | throw e 367 | end 368 | end 369 | 370 | def to_pickle 371 | # rewrite jumps to use python bytecode ordering 372 | @jumps.each do |n, jset| 373 | jset.each do |jump| 374 | idx = @bytecode.index(jump) 375 | pos = @bytecode[0,idx].flatten.length 376 | jump[1] = (@labels[n] - pos) - jump.length 377 | end 378 | # @jumps[1] = @labels[n] 379 | end 380 | 381 | f = "c" 382 | f << [@argc, @varsyms.length, @stacksize, @flags].pack("LLLL") 383 | 384 | # bytecode 385 | bytes = @bytecode.flatten 386 | f << "s" << bytes.length.to_plong 387 | f << bytes.pack("c*") 388 | 389 | # constants 390 | f << "(" << @consts.length.to_plong 391 | @consts.each do |c| 392 | f << c.to_pickle 393 | end 394 | 395 | # names 396 | f << "(" << @symbols.length.to_plong 397 | @symbols.each do |n| 398 | f << n.to_pickle 399 | end 400 | 401 | # varnames 402 | f << "(" << @varsyms.length.to_plong 403 | @varsyms.each do |n| 404 | f << n.to_pickle 405 | end 406 | 407 | # freevars 408 | f << "(" << 0.to_plong 409 | 410 | # cellvars 411 | f << "(" << 0.to_plong 412 | 413 | # metadata 414 | f << @filename.to_pickle 415 | f << @name.to_pickle 416 | f << 1.to_plong 417 | 418 | lnotab = "" 419 | lastn, lastpos = @lines[0] 420 | @lines[1..-1].each do |n, pos| 421 | lnotab << [pos - lastpos, n - lastn].pack("cc") 422 | lastn, lastpos = n, pos 423 | end 424 | f << lnotab.to_pickle 425 | f 426 | end 427 | 428 | def compile(fname) 429 | # dump the bytecode 430 | File.open(fname, "wb") do |f| 431 | f << "\xB3\xF2\r\n" # magic number for python 2.5 432 | f << Time.now.to_i.to_plong 433 | 434 | # code object 435 | f << self.to_pickle 436 | end 437 | end 438 | end 439 | -------------------------------------------------------------------------------- /decompyle/decompyle/optimizer.py: -------------------------------------------------------------------------------- 1 | """A bytecode optimizer transliterated from compile.c to ease maintenance. 2 | 3 | $Id: optimizer.py 82 2006-10-21 00:54:34Z spencermah $""" 4 | 5 | import opcode 6 | from array import array 7 | 8 | COMPATIBILITY = True 9 | MAX_CONST_FOLD_SIZE = 20 10 | 11 | 12 | def markblocks(codestr): 13 | """Identify blocks within which code may be safely altered. See 14 | ISBASICBLOCK for the use cases""" 15 | 16 | from .se import ( 17 | HAS_JUMP, 18 | GETJUMPTGT, 19 | HAVE_ARGUMENT) 20 | 21 | blocks = [0] * len(codestr) 22 | i = 0 23 | codelen = len(codestr) 24 | while i=HAVE_ARGUMENT) else 1) 30 | i=0 31 | blockcnt=0 32 | while i 32700: 100 | raise OptimizerError('code too big (>32700 characters)') 101 | elif codestr[-1] != RETURN_VALUE: 102 | raise OptimizerError('code does not end with RETURN_VALUE') 103 | 104 | cumlc=0 # cumulative LOAD_CONSTS 105 | i = 0 106 | 107 | while iHAVE_ARGUMENT 112 | # However, this means that if an opcode is changed in place, 113 | # op must be updated too. Subtle bug potential! 114 | 115 | 116 | lastlc = cumlc 117 | cumlc = 0; 118 | op = codestr[i] 119 | 120 | # Replace UNARY_NOT JUMP_IF_FALSE POP_TOP with 121 | # JUMP_IF_TRUE POP_TOP */ 122 | # This appears not to work in compile.c 123 | if op == UNARY_NOT: 124 | if (codestr[i+1] == JUMP_IF_FALSE and 125 | codestr[i+4] == POP_TOP and 126 | ISBASICBLOCK(blocks,i,5)): 127 | tgt = GETJUMPTGT(codestr, (i+1)) 128 | if (codestr[tgt] == POP_TOP): 129 | j = GETARG(codestr, i+1) + 1 130 | op = codestr[i] = JUMP_IF_TRUE 131 | SETARG(codestr, i, j) 132 | codestr[i+3] = POP_TOP 133 | codestr[i+4] = NOP 134 | 135 | # not a is b --> a is not b 136 | # not a in b --> a not in b 137 | # not a is not b --> a is b 138 | # not a not in b --> a in b 139 | elif op == COMPARE_OP: 140 | j = GETARG(codestr, i) 141 | if 6 <= j <= 9 and codestr[i+3] == UNARY_NOT and \ 142 | ISBASICBLOCK(blocks,i,4): 143 | 144 | SETARG(codestr, i, (j^1)) 145 | codestr[i+3] = NOP 146 | 147 | # Replace LOAD_GLOBAL/LOAD_NAME None with LOAD_CONST None 148 | elif op == LOAD_NAME or op == LOAD_GLOBAL: 149 | j = GETARG(codestr, i) 150 | if names[j] == 'None': 151 | if (not COMPATIBILITY) or (None in consts): 152 | # CPython won't add None 153 | 154 | op = codestr[i] = LOAD_CONST 155 | SETARG(codestr, i, consts.index_or_add(None)) 156 | cumlc = lastlc + 1 157 | 158 | # Skip over LOAD_CONST trueconst JUMP_IF_FALSE xx POP_TOP */ 159 | elif op == LOAD_CONST: 160 | cumlc = lastlc + 1; 161 | j = GETARG(codestr, i); 162 | if (codestr[i+3] == JUMP_IF_FALSE and 163 | codestr[i+6] == POP_TOP and 164 | ISBASICBLOCK(blocks, i, 7) and 165 | consts[j]): 166 | codestr[i:i+7] = NOP_CELL*7 167 | cumlc = 0; 168 | 169 | 170 | # Try to fold tuples of constants (includes a case for lists 171 | # which are only used for "in" and "not in" tests). 172 | # Skip over BUILD_SEQN 1 UNPACK_SEQN 1. 173 | # Replace BUILD_SEQN 2 UNPACK_SEQN 2 with ROT2. 174 | # Replace BUILD_SEQN 3 UNPACK_SEQN 3 with ROT3 ROT2. 175 | elif op == BUILD_TUPLE or op == BUILD_LIST: 176 | j = GETARG(codestr, i) 177 | h = i - 3 * j; 178 | 179 | if h >= 0 and j <= lastlc and ( 180 | (op == BUILD_TUPLE and ISBASICBLOCK(blocks, h, 3*(j+1))) 181 | or (op == BUILD_LIST and codestr[i+3]==COMPARE_OP and \ 182 | ISBASICBLOCK(blocks, h, 3*(j+2)) and (6<=GETARG(codestr, i+3)<=7))): 183 | 184 | # in-line tuple_of_constants(codestr, h, j, consts) 185 | 186 | end = h + j*3 187 | newconst = [] 188 | 189 | combined_const = tuple(consts[GETARG(codestr,i)] 190 | for i in range(h, end, 3)) 191 | 192 | new_arg = consts.add(combined_const, force_append = COMPATIBILITY) 193 | 194 | 195 | # Write NOPs over old LOAD_CONSTS and 196 | # add a new LOAD_CONST newconst on top of the BUILD_TUPLE n */ 197 | 198 | codestr[h:end] = NOP_CELL*(j*3) 199 | codestr[end] = LOAD_CONST; 200 | SETARG(codestr, end, new_arg) # compile.c doesn't check to see i 201 | # new constant already exists, so this will diverge in some cases 202 | 203 | 204 | assert(codestr[i] == LOAD_CONST) 205 | cumlc = 1 206 | 207 | elif (codestr[i+3] == UNPACK_SEQUENCE and 208 | ISBASICBLOCK(blocks, i, 6) and 209 | j == GETARG(codestr, i+3)): 210 | 211 | if j == 1: 212 | codestr[i:i+6] = NOP_CELL*6 213 | elif j == 2: 214 | op = codestr[i] = ROT_TWO 215 | codestr[i+1:i+6] = NOP_CELL*5 216 | elif j == 3: 217 | op = codestr[i] = ROT_THREE 218 | codestr[i+1] = ROT_TWO 219 | codestr[i+2:i+6] = NOP_CELL*4 220 | 221 | # Fold binary ops on constants. 222 | # LOAD_CONST c1 LOAD_CONST c2 BINOP --> LOAD_CONST binop(c1,c2) 223 | 224 | elif op in BINOPS: 225 | # BINOPS maps op:function 226 | if (lastlc >= 2 and ISBASICBLOCK(blocks, i-6, 7)): 227 | # inline fold_binops_on_constants(&codestr[i-6], consts)) 228 | start = i-6 229 | #consts_tuple = consts.inorder() 230 | const1 = consts[GETARG(codestr, start)] 231 | const2 = consts[GETARG(codestr, start+3)] 232 | # compile.c does not have this test - how does it avoid 233 | # crashing on /0? 234 | try: 235 | new_const = BINOPS[op](const1, const2) 236 | except (TypeError, ZeroDivisionError, ValueError): 237 | pass 238 | else: 239 | size = 0 240 | if hasattr(new_const,"__len__"): 241 | size = len(new_const) 242 | if size <= MAX_CONST_FOLD_SIZE: 243 | # compile.c does a size check? what is this? 244 | i = start+4 # ~ i-=2 245 | codestr[start:i] = NOP_CELL*4 246 | 247 | op = codestr[i]=LOAD_CONST # op = 248 | # here, compile.c just appends to consts 249 | SETARG(codestr, i, consts.add(new_const, force_append = COMPATIBILITY)) 250 | cumlc = 1 251 | 252 | # Fold unary ops on constants. 253 | # LOAD_CONST c1 UNARY_OP --> LOAD_CONST unary_op(c) */ 254 | elif op in UNARY_OPS: 255 | # UNARY_OPS maps opcode:function 256 | if lastlc >= 1 and ISBASICBLOCK(blocks, i-3, 4): 257 | # inline fold_unaryops_on_constants 258 | start = i-3 259 | const1 = consts[GETARG(codestr, start)] 260 | if const1 or op != UNARY_NEGATIVE: 261 | # Don't negate -0.0 262 | new_const = UNARY_OPS[op](const1) 263 | i = start+1 264 | codestr[start] = NOP 265 | op = codestr[i] = LOAD_CONST 266 | SETARG(codestr, i, consts.add(new_const, force_append = COMPATIBILITY)) 267 | cumlc = 1 268 | 269 | # Simplify conditional jump to conditional jump where the 270 | # result of the first test implies the success of a similar 271 | # test or the failure of the opposite test. 272 | # Arises in code like: 273 | # "if a and b:" 274 | # "if a or b:" 275 | # "a and b or c" 276 | # "(a and b) and c" 277 | # x:JUMP_IF_FALSE y y:JUMP_IF_FALSE z --> x:JUMP_IF_FALSE z 278 | # x:JUMP_IF_FALSE y y:JUMP_IF_TRUE z --> x:JUMP_IF_FALSE y+3 279 | # where y+3 is the instruction following the second test. 280 | elif op == JUMP_IF_FALSE or op == JUMP_IF_TRUE: 281 | tgt = GETJUMPTGT(codestr, i) 282 | j = codestr[tgt] 283 | if j == JUMP_IF_FALSE or j == JUMP_IF_TRUE: 284 | if j == op: 285 | tgttgt = GETJUMPTGT(codestr, tgt) - i - 3 286 | SETARG(codestr, i, tgttgt) 287 | else: 288 | tgt -= i 289 | SETARG(codestr, i, tgt) 290 | 291 | if j in HAS_DIRECT_JUMP: 292 | tgttgt = GETJUMPTGT(codestr, tgt) 293 | if op == JUMP_FORWARD: 294 | op = JUMP_ABSOLUTE 295 | if op not in HAS_ABS_JUMP: 296 | tgttgt -= (i+3) 297 | if tgttgt >= 0: 298 | codestr[i] = op 299 | SETARG(codestr, i, tgttgt) 300 | 301 | # Untested? 302 | elif op in HAS_UNCONDITIONAL_JUMP: 303 | tgt = GETJUMPTGT(codestr, i) 304 | 305 | if codestr[tgt] in HAS_DIRECT_JUMP: 306 | tgttgt = GETJUMPTGT(codestr, tgt) 307 | if op == JUMP_FORWARD: 308 | op = JUMP_ABSOLUTE 309 | if op not in HAS_ABS_JUMP: 310 | tgttgt -= (i+3) 311 | if tgttgt >= 0: 312 | codestr[i] = op 313 | SETARG(codestr, i, tgttgt) 314 | 315 | elif op == EXTENDED_ARG: 316 | raise OptimizerError("Found EXTENDED_ARG") 317 | 318 | elif op == RETURN_VALUE: 319 | if (i+4 < codelen and codestr[i+4] == RETURN_VALUE 320 | and ISBASICBLOCK(blocks, i, 5)): 321 | for j in range(i+1, i+5): 322 | codestr[j] = NOP 323 | 324 | i += 1 + 2*(op>=HAVE_ARGUMENT) 325 | 326 | # Fixup linenotab 327 | nops = 0 328 | i=0 329 | while i=HAVE_ARGUMENT) 334 | 335 | cum_orig_line = 0 336 | last_line = 0 337 | 338 | i=0 339 | while i=HAVE_ARGUMENT) 360 | codestr[h:h+adj] = codestr[i:i+adj] 361 | i+=adj; h+=adj 362 | 363 | assert h+nops == codelen 364 | del codestr[h:] 365 | except OptimizerError: 366 | pass 367 | # bail out 368 | else: 369 | # copy the code back 370 | codestr_orig[:] = codestr 371 | -------------------------------------------------------------------------------- /decompyle/decompyle/new-Parser.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 1999 John Aycock 2 | # Copyright (c) 2000-2002 by hartmut Goebel 3 | # 4 | # See main module for license. 5 | # 6 | 7 | __all__ = ['parse', 'AST', 'ParserError', 'Parser'] 8 | 9 | from spark import GenericASTBuilder, GenericASTMatcher 10 | import string, exceptions, sys 11 | from UserList import UserList 12 | 13 | from Scanner import Token 14 | 15 | class AST(UserList): 16 | def __init__(self, type, kids=[]): 17 | self.type = intern(type) 18 | UserList.__init__(self, kids) 19 | 20 | def __getslice__(self, low, high): return self.data[low:high] 21 | def __eq__(self, o): 22 | if isinstance(o, AST): 23 | return self.type == o.type \ 24 | and UserList.__eq__(self, o) 25 | else: 26 | return self.type == o 27 | 28 | def __hash__(self): return hash(self.type) 29 | 30 | def __repr__(self, indent=''): 31 | rv = str(self.type) 32 | for k in self: 33 | rv = rv + '\n' + string.replace(str(k), '\n', '\n ') 34 | return rv 35 | 36 | 37 | class ParserError(Exception): 38 | def __init__(self, token, offset): 39 | self.token = token 40 | self.offset = offset 41 | 42 | def __str__(self): 43 | return "Syntax error at or near `%r' token at offset %s" % \ 44 | (self.token, self.offset) 45 | 46 | 47 | class Parser(GenericASTBuilder): 48 | def __init__(self): 49 | GenericASTBuilder.__init__(self, AST, 'stmts') 50 | self.customized = {} 51 | 52 | def cleanup(self): 53 | """ 54 | Remove recursive references to allow garbage 55 | collector to collect this object. 56 | """ 57 | for dict in (self.rule2func, self.rules, self.rule2name, self.first): 58 | for i in dict.keys(): 59 | dict[i] = None 60 | for i in dir(self): 61 | setattr(self, i, None) 62 | 63 | def error(self, token): 64 | raise ParserError(token, token.offset) 65 | 66 | def typestring(self, token): 67 | return token.type 68 | 69 | def p_funcdef(self, args): 70 | ''' 71 | stmt ::= funcdef 72 | funcdef ::= mkfunc designator 73 | load_closure ::= load_closure LOAD_CLOSURE 74 | load_closure ::= LOAD_CLOSURE 75 | ''' 76 | 77 | def p_list_comprehension(self, args): 78 | ''' 79 | expr ::= list_compr 80 | list_compr ::= BUILD_LIST_0 DUP_TOP LOAD_ATTR 81 | designator list_iter del_stmt 82 | list_compr ::= BUILD_LIST_0 DUP_TOP 83 | designator list_iter del_stmt 84 | 85 | list_iter ::= list_for 86 | list_iter ::= list_if 87 | list_iter ::= lc_body 88 | 89 | list_for ::= expr _for designator list_iter 90 | JUMP_ABSOLUTE COME_FROM 91 | list_if ::= expr condjmp list_iter 92 | JUMP_FORWARD COME_FROM POP_TOP 93 | COME_FROM 94 | list_if ::= expr condjmp list_iter 95 | JUMP_ABSOLUTE COME_FROM POP_TOP 96 | 97 | 98 | 99 | lc_body ::= LOAD_NAME expr CALL_FUNCTION_1 POP_TOP 100 | lc_body ::= LOAD_FAST expr CALL_FUNCTION_1 POP_TOP 101 | lc_body ::= LOAD_NAME expr LIST_APPEND 102 | lc_body ::= LOAD_FAST expr LIST_APPEND 103 | ''' 104 | 105 | def p_augmented_assign(self, args): 106 | ''' 107 | stmt ::= augassign1 108 | stmt ::= augassign2 109 | augassign1 ::= expr expr inplace_op designator 110 | augassign1 ::= expr expr inplace_op ROT_THREE STORE_SUBSCR 111 | augassign1 ::= expr expr inplace_op ROT_TWO STORE_SLICE+0 112 | augassign1 ::= expr expr inplace_op ROT_THREE STORE_SLICE+1 113 | augassign1 ::= expr expr inplace_op ROT_THREE STORE_SLICE+2 114 | augassign1 ::= expr expr inplace_op ROT_FOUR STORE_SLICE+3 115 | augassign2 ::= expr DUP_TOP LOAD_ATTR expr 116 | inplace_op ROT_TWO STORE_ATTR 117 | 118 | inplace_op ::= INPLACE_ADD 119 | inplace_op ::= INPLACE_SUBTRACT 120 | inplace_op ::= INPLACE_MULTIPLY 121 | inplace_op ::= INPLACE_DIVIDE 122 | inplace_op ::= INPLACE_TRUE_DIVIDE 123 | inplace_op ::= INPLACE_FLOOR_DIVIDE 124 | inplace_op ::= INPLACE_MODULO 125 | inplace_op ::= INPLACE_POWER 126 | inplace_op ::= INPLACE_LSHIFT 127 | inplace_op ::= INPLACE_RSHIFT 128 | inplace_op ::= INPLACE_AND 129 | inplace_op ::= INPLACE_XOR 130 | inplace_op ::= INPLACE_OR 131 | ''' 132 | 133 | def p_assign(self, args): 134 | ''' 135 | stmt ::= assign 136 | assign ::= expr DUP_TOP designList 137 | assign ::= expr designator 138 | ''' 139 | 140 | def p_print(self, args): 141 | ''' 142 | stmt ::= print_stmt 143 | stmt ::= print_stmt_nl 144 | stmt ::= print_nl_stmt 145 | print_stmt ::= expr PRINT_ITEM 146 | print_nl_stmt ::= PRINT_NEWLINE 147 | print_stmt_nl ::= print_stmt print_nl_stmt 148 | ''' 149 | 150 | def p_print_to(self, args): 151 | ''' 152 | stmt ::= print_to 153 | stmt ::= print_to_nl 154 | stmt ::= print_nl_to 155 | print_to ::= expr print_to_items POP_TOP 156 | print_to_nl ::= expr print_to_items PRINT_NEWLINE_TO 157 | print_nl_to ::= expr PRINT_NEWLINE_TO 158 | print_to_items ::= print_to_items print_to_item 159 | print_to_items ::= print_to_item 160 | print_to_item ::= DUP_TOP expr ROT_TWO PRINT_ITEM_TO 161 | ''' 162 | # expr print_to* POP_TOP 163 | # expr { print_to* } PRINT_NEWLINE_TO 164 | 165 | def p_import15(self, args): 166 | ''' 167 | stmt ::= importstmt 168 | stmt ::= importfrom 169 | 170 | importstmt ::= IMPORT_NAME STORE_FAST 171 | importstmt ::= IMPORT_NAME STORE_NAME 172 | 173 | importfrom ::= IMPORT_NAME importlist POP_TOP 174 | importlist ::= importlist IMPORT_FROM 175 | importlist ::= IMPORT_FROM 176 | ''' 177 | 178 | def p_import20(self, args): 179 | ''' 180 | stmt ::= importstmt2 181 | stmt ::= importfrom2 182 | stmt ::= importstar2 183 | 184 | importstmt2 ::= LOAD_CONST import_as 185 | importstar2 ::= LOAD_CONST IMPORT_NAME IMPORT_STAR 186 | 187 | importfrom2 ::= LOAD_CONST IMPORT_NAME importlist2 POP_TOP 188 | importlist2 ::= importlist2 import_as 189 | importlist2 ::= import_as 190 | import_as ::= IMPORT_NAME designator 191 | import_as ::= IMPORT_NAME LOAD_ATTR designator 192 | import_as ::= IMPORT_FROM designator 193 | ''' 194 | 195 | def p_grammar(self, args): 196 | ''' 197 | stmts ::= stmts stmt 198 | stmts ::= stmt 199 | 200 | stmts_opt ::= stmts 201 | stmts_opt ::= passstmt 202 | passstmt ::= 203 | 204 | designList ::= designator designator 205 | designList ::= designator DUP_TOP designList 206 | 207 | designator ::= STORE_FAST 208 | designator ::= STORE_NAME 209 | designator ::= STORE_GLOBAL 210 | designator ::= STORE_DEREF 211 | designator ::= expr STORE_ATTR 212 | designator ::= expr STORE_SLICE+0 213 | designator ::= expr expr STORE_SLICE+1 214 | designator ::= expr expr STORE_SLICE+2 215 | designator ::= expr expr expr STORE_SLICE+3 216 | designator ::= store_subscr 217 | store_subscr ::= expr expr STORE_SUBSCR 218 | designator ::= unpack 219 | designator ::= unpack_list 220 | 221 | stmt ::= classdef 222 | stmt ::= call_stmt 223 | call_stmt ::= expr POP_TOP 224 | 225 | stmt ::= return_stmt 226 | return_stmt ::= expr RETURN_VALUE 227 | 228 | stmt ::= yield_stmt 229 | yield_stmt ::= expr YIELD_STMT 230 | 231 | stmt ::= break_stmt 232 | break_stmt ::= BREAK_LOOP 233 | 234 | stmt ::= continue_stmt 235 | continue_stmt ::= JUMP_ABSOLUTE 236 | continue_stmt ::= CONTINUE_LOOP 237 | 238 | stmt ::= raise_stmt 239 | raise_stmt ::= exprlist RAISE_VARARGS 240 | raise_stmt ::= nullexprlist RAISE_VARARGS 241 | 242 | stmt ::= exec_stmt 243 | exec_stmt ::= expr exprlist DUP_TOP EXEC_STMT 244 | exec_stmt ::= expr exprlist EXEC_STMT 245 | 246 | stmt ::= assert 247 | stmt ::= assert2 248 | stmt ::= ifstmt 249 | stmt ::= ifelsestmt 250 | stmt ::= whilestmt 251 | stmt ::= whileelsestmt 252 | stmt ::= forstmt 253 | stmt ::= forelsestmt 254 | stmt ::= trystmt 255 | stmt ::= tryfinallystmt 256 | 257 | stmt ::= del_stmt 258 | del_stmt ::= DELETE_FAST 259 | del_stmt ::= DELETE_NAME 260 | del_stmt ::= DELETE_GLOBAL 261 | del_stmt ::= expr DELETE_SLICE+0 262 | del_stmt ::= expr expr DELETE_SLICE+1 263 | del_stmt ::= expr expr DELETE_SLICE+2 264 | del_stmt ::= expr expr expr DELETE_SLICE+3 265 | del_stmt ::= delete_subscr 266 | delete_subscr ::= expr expr DELETE_SUBSCR 267 | del_stmt ::= expr DELETE_ATTR 268 | 269 | kwarg ::= LOAD_CONST expr 270 | 271 | classdef ::= LOAD_CONST expr mkfunc 272 | CALL_FUNCTION_0 BUILD_CLASS designator 273 | 274 | condjmp ::= JUMP_IF_FALSE POP_TOP 275 | condjmp ::= JUMP_IF_TRUE POP_TOP 276 | 277 | assert ::= expr JUMP_IF_FALSE POP_TOP 278 | expr JUMP_IF_TRUE POP_TOP 279 | LOAD_GLOBAL RAISE_VARARGS 280 | COME_FROM COME_FROM POP_TOP 281 | assert2 ::= expr JUMP_IF_FALSE POP_TOP 282 | expr JUMP_IF_TRUE POP_TOP 283 | LOAD_GLOBAL expr RAISE_VARARGS 284 | COME_FROM COME_FROM POP_TOP 285 | 286 | ifstmt ::= expr condjmp stmts_opt 287 | JUMP_FORWARD COME_FROM POP_TOP 288 | COME_FROM 289 | 290 | ifelsestmt ::= expr condjmp stmts_opt 291 | JUMP_FORWARD COME_FROM 292 | POP_TOP stmts COME_FROM 293 | 294 | trystmt ::= SETUP_EXCEPT stmts_opt 295 | POP_BLOCK JUMP_FORWARD 296 | COME_FROM except_stmt 297 | trystmt ::= SETUP_EXCEPT stmts_opt 298 | POP_BLOCK JUMP_ABSOLUTE 299 | COME_FROM except_stmt 300 | 301 | try_end ::= END_FINALLY 302 | try_end ::= END_FINALLY COME_FROM 303 | try_end ::= except_else 304 | except_else ::= END_FINALLY COME_FROM stmts 305 | 306 | except_stmt ::= except_cond except_stmt COME_FROM 307 | except_stmt ::= except_conds try_end COME_FROM 308 | except_stmt ::= except try_end COME_FROM 309 | except_stmt ::= except try_end 310 | except_stmt ::= try_end 311 | 312 | except_conds ::= except_cond except_conds COME_FROM 313 | except_conds ::= 314 | 315 | except_cond ::= except_cond1 316 | except_cond ::= except_cond2 317 | except_cond1 ::= DUP_TOP expr COMPARE_OP 318 | JUMP_IF_FALSE 319 | POP_TOP POP_TOP POP_TOP POP_TOP 320 | stmts_opt JUMP_FORWARD COME_FROM 321 | POP_TOP 322 | except_cond2 ::= DUP_TOP expr COMPARE_OP 323 | JUMP_IF_FALSE 324 | POP_TOP POP_TOP designator POP_TOP 325 | stmts_opt JUMP_FORWARD COME_FROM 326 | POP_TOP 327 | except ::= POP_TOP POP_TOP POP_TOP 328 | stmts_opt JUMP_FORWARD 329 | except ::= POP_TOP POP_TOP POP_TOP 330 | stmts_opt JUMP_ABSOLUTE 331 | 332 | tryfinallystmt ::= SETUP_FINALLY stmts_opt 333 | POP_BLOCK LOAD_CONST 334 | COME_FROM stmts_opt END_FINALLY 335 | 336 | whilestmt ::= SETUP_LOOP 337 | expr JUMP_IF_FALSE POP_TOP 338 | stmts_opt JUMP_ABSOLUTE 339 | COME_FROM POP_TOP POP_BLOCK COME_FROM 340 | whileelsestmt ::= SETUP_LOOP 341 | expr JUMP_IF_FALSE POP_TOP 342 | stmts_opt JUMP_ABSOLUTE 343 | COME_FROM POP_TOP POP_BLOCK 344 | stmts COME_FROM 345 | 346 | _for ::= GET_ITER FOR_ITER 347 | _for ::= LOAD_CONST FOR_LOOP 348 | 349 | forstmt ::= SETUP_LOOP expr _for designator 350 | stmts_opt JUMP_ABSOLUTE 351 | COME_FROM POP_BLOCK COME_FROM 352 | 353 | forelsestmt ::= SETUP_LOOP expr _for designator 354 | stmts_opt JUMP_ABSOLUTE 355 | COME_FROM POP_BLOCK stmts COME_FROM 356 | 357 | ''' 358 | 359 | def p_expr(self, args): 360 | ''' 361 | expr ::= load_closure mklambda 362 | expr ::= mklambda 363 | expr ::= SET_LINENO 364 | expr ::= LOAD_FAST 365 | expr ::= LOAD_NAME 366 | expr ::= LOAD_CONST 367 | expr ::= LOAD_GLOBAL 368 | expr ::= LOAD_DEREF 369 | expr ::= LOAD_LOCALS 370 | expr ::= expr LOAD_ATTR 371 | expr ::= binary_expr 372 | expr ::= build_list 373 | 374 | binary_expr ::= expr expr binary_op 375 | binary_op ::= BINARY_ADD 376 | binary_op ::= BINARY_SUBTRACT 377 | binary_op ::= BINARY_MULTIPLY 378 | binary_op ::= BINARY_DIVIDE 379 | binary_op ::= BINARY_TRUE_DIVIDE 380 | binary_op ::= BINARY_FLOOR_DIVIDE 381 | binary_op ::= BINARY_MODULO 382 | binary_op ::= BINARY_LSHIFT 383 | binary_op ::= BINARY_RSHIFT 384 | binary_op ::= BINARY_AND 385 | binary_op ::= BINARY_OR 386 | binary_op ::= BINARY_XOR 387 | binary_op ::= BINARY_POWER 388 | 389 | expr ::= binary_subscr 390 | binary_subscr ::= expr expr BINARY_SUBSCR 391 | expr ::= expr expr DUP_TOPX_2 BINARY_SUBSCR 392 | expr ::= cmp 393 | expr ::= expr UNARY_POSITIVE 394 | expr ::= expr UNARY_NEGATIVE 395 | expr ::= expr UNARY_CONVERT 396 | expr ::= expr UNARY_INVERT 397 | expr ::= expr UNARY_NOT 398 | expr ::= mapexpr 399 | expr ::= expr SLICE+0 400 | expr ::= expr expr SLICE+1 401 | expr ::= expr expr SLICE+2 402 | expr ::= expr expr expr SLICE+3 403 | expr ::= expr DUP_TOP SLICE+0 404 | expr ::= expr expr DUP_TOPX_2 SLICE+1 405 | expr ::= expr expr DUP_TOPX_2 SLICE+2 406 | expr ::= expr expr expr DUP_TOPX_3 SLICE+3 407 | expr ::= and 408 | expr ::= or 409 | or ::= expr JUMP_IF_TRUE POP_TOP expr COME_FROM 410 | and ::= expr JUMP_IF_FALSE POP_TOP expr COME_FROM 411 | 412 | cmp ::= cmp_list 413 | cmp ::= compare 414 | compare ::= expr expr COMPARE_OP 415 | cmp_list ::= expr cmp_list1 ROT_TWO POP_TOP 416 | COME_FROM 417 | cmp_list1 ::= expr DUP_TOP ROT_THREE 418 | COMPARE_OP JUMP_IF_FALSE POP_TOP 419 | cmp_list1 COME_FROM 420 | cmp_list1 ::= expr DUP_TOP ROT_THREE 421 | COMPARE_OP JUMP_IF_FALSE POP_TOP 422 | cmp_list2 COME_FROM 423 | cmp_list2 ::= expr COMPARE_OP JUMP_FORWARD 424 | mapexpr ::= BUILD_MAP kvlist 425 | 426 | kvlist ::= kvlist kv 427 | kvlist ::= 428 | 429 | kv ::= DUP_TOP expr ROT_TWO expr STORE_SUBSCR 430 | kv ::= DUP_TOP expr expr ROT_THREE STORE_SUBSCR 431 | 432 | exprlist ::= exprlist expr 433 | exprlist ::= expr 434 | 435 | nullexprlist ::= 436 | ''' 437 | 438 | def nonterminal(self, nt, args): 439 | collect = ('stmts', 'exprlist', 'kvlist') 440 | 441 | if nt in collect and len(args) > 1: 442 | # 443 | # Collect iterated thingies together. 444 | # 445 | rv = args[0] 446 | rv.append(args[1]) 447 | else: 448 | rv = GenericASTBuilder.nonterminal(self, nt, args) 449 | return rv 450 | 451 | def __ambiguity(self, children): 452 | # only for debugging! to be removed hG/2000-10-15 453 | print children 454 | return GenericASTBuilder.ambiguity(self, children) 455 | 456 | def resolve(self, list): 457 | if len(list) == 2 and 'funcdef' in list and 'assign' in list: 458 | return 'funcdef' 459 | #print >> sys.stderr, 'resolve', str(list) 460 | return GenericASTBuilder.resolve(self, list) 461 | 462 | nop = lambda self, args: None 463 | 464 | p = Parser() 465 | 466 | def parse(tokens, customize): 467 | # 468 | # Special handling for opcodes that take a variable number 469 | # of arguments -- we add a new rule for each: 470 | # 471 | # expr ::= {expr}^n BUILD_LIST_n 472 | # expr ::= {expr}^n BUILD_TUPLE_n 473 | # expr ::= {expr}^n BUILD_SLICE_n 474 | # unpack_list ::= UNPACK_LIST {expr}^n 475 | # unpack ::= UNPACK_TUPLE {expr}^n 476 | # unpack ::= UNPACK_SEQEUENE {expr}^n 477 | # mkfunc ::= {expr}^n LOAD_CONST MAKE_FUNCTION_n 478 | # mkfunc ::= {expr}^n load_closure LOAD_CONST MAKE_FUNCTION_n 479 | # expr ::= expr {expr}^n CALL_FUNCTION_n 480 | # expr ::= expr {expr}^n CALL_FUNCTION_VAR_n POP_TOP 481 | # expr ::= expr {expr}^n CALL_FUNCTION_VAR_KW_n POP_TOP 482 | # expr ::= expr {expr}^n CALL_FUNCTION_KW_n POP_TOP 483 | # 484 | global p 485 | for k, v in customize.items(): 486 | # avoid adding the same rule twice to this parser 487 | if p.customized.has_key(k): 488 | continue 489 | p.customized[k] = None 490 | 491 | #nop = lambda self, args: None 492 | op = k[:string.rfind(k, '_')] 493 | if op in ('BUILD_LIST', 'BUILD_TUPLE'): 494 | rule = 'build_list ::= ' + 'expr '*v + k 495 | elif op == 'BUILD_SLICE': 496 | rule = 'expr ::= ' + 'expr '*v + k 497 | elif op in ('UNPACK_TUPLE', 'UNPACK_SEQUENCE'): 498 | rule = 'unpack ::= ' + k + ' designator'*v 499 | elif op == 'UNPACK_LIST': 500 | rule = 'unpack_list ::= ' + k + ' designator'*v 501 | elif op == 'DUP_TOPX': 502 | # no need to add a rule 503 | continue 504 | #rule = 'dup_topx ::= ' + 'expr '*v + k 505 | elif op == 'MAKE_FUNCTION': 506 | p.addRule('mklambda ::= %s LOAD_LAMBDA %s' % 507 | ('expr '*v, k), nop) 508 | rule = 'mkfunc ::= %s LOAD_CONST %s' % ('expr '*v, k) 509 | elif op == 'MAKE_CLOSURE': 510 | p.addRule('mklambda ::= %s load_closure LOAD_LAMBDA %s' % 511 | ('expr '*v, k), nop) 512 | rule = 'mkfunc ::= %s load_closure LOAD_CONST %s' % ('expr '*v, k) 513 | elif op in ('CALL_FUNCTION', 'CALL_FUNCTION_VAR', 514 | 'CALL_FUNCTION_VAR_KW', 'CALL_FUNCTION_KW'): 515 | na = (v & 0xff) # positional parameters 516 | nk = (v >> 8) & 0xff # keyword parameters 517 | # number of apply equiv arguments: 518 | nak = ( len(op)-len('CALL_FUNCTION') ) / 3 519 | rule = 'expr ::= expr ' + 'expr '*na + 'kwarg '*nk \ 520 | + 'expr ' * nak + k 521 | else: 522 | raise 'unknown customize token %s' % k 523 | p.addRule(rule, nop) 524 | ast = p.parse(tokens) 525 | #p.cleanup() 526 | return ast 527 | 528 | # local variables: 529 | # tab-width: 4 530 | -------------------------------------------------------------------------------- /decompyle/decompyle/spark.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 1998-2000 John Aycock 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining 4 | # a copy of this software and associated documentation files (the 5 | # "Software"), to deal in the Software without restriction, including 6 | # without limitation the rights to use, copy, modify, merge, publish, 7 | # distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so, subject to 9 | # the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be 12 | # included in all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | __version__ = 'SPARK-0.6.1' 23 | 24 | import re 25 | import sys 26 | import string 27 | 28 | def _namelist(instance): 29 | namelist, namedict, classlist = [], {}, [instance.__class__] 30 | for c in classlist: 31 | for b in c.__bases__: 32 | classlist.append(b) 33 | #for name in dir(c): # dir() behavior changed in 2.2 34 | for name in c.__dict__.keys(): # <-- USE THIS 35 | if not namedict.has_key(name): 36 | namelist.append(name) 37 | namedict[name] = 1 38 | return namelist 39 | 40 | class GenericScanner: 41 | def __init__(self): 42 | pattern = self.reflect() 43 | self.re = re.compile(pattern, re.VERBOSE) 44 | 45 | self.index2func = {} 46 | for name, number in self.re.groupindex.items(): 47 | self.index2func[number-1] = getattr(self, 't_' + name) 48 | 49 | def makeRE(self, name): 50 | doc = getattr(self, name).__doc__ 51 | rv = '(?P<%s>%s)' % (name[2:], doc) 52 | return rv 53 | 54 | def reflect(self): 55 | rv = [] 56 | for name in _namelist(self): 57 | if name[:2] == 't_' and name != 't_default': 58 | rv.append(self.makeRE(name)) 59 | 60 | rv.append(self.makeRE('t_default')) 61 | return string.join(rv, '|') 62 | 63 | def error(self, s, pos): 64 | print "Lexical error at position %s" % pos 65 | raise SystemExit 66 | 67 | def tokenize(self, s): 68 | pos = 0 69 | n = len(s) 70 | while pos < n: 71 | m = self.re.match(s, pos) 72 | if m is None: 73 | self.error(s, pos) 74 | 75 | groups = m.groups() 76 | for i in range(len(groups)): 77 | if groups[i] and self.index2func.has_key(i): 78 | self.index2func[i](groups[i]) 79 | pos = m.end() 80 | 81 | def t_default(self, s): 82 | r'( . | \n )+' 83 | pass 84 | 85 | class GenericParser: 86 | def __init__(self, start): 87 | self.rules = {} 88 | self.rule2func = {} 89 | self.rule2name = {} 90 | self.collectRules() 91 | self.startRule = self.augment(start) 92 | self.ruleschanged = 1 93 | 94 | _START = 'START' 95 | _EOF = 'EOF' 96 | 97 | # 98 | # A hook for GenericASTBuilder and GenericASTMatcher. 99 | # 100 | def preprocess(self, rule, func): return rule, func 101 | 102 | def addRule(self, doc, func): 103 | rules = string.split(doc) 104 | 105 | index = [] 106 | for i in range(len(rules)): 107 | if rules[i] == '::=': 108 | index.append(i-1) 109 | index.append(len(rules)) 110 | 111 | for i in range(len(index)-1): 112 | lhs = rules[index[i]] 113 | rhs = rules[index[i]+2:index[i+1]] 114 | rule = (lhs, tuple(rhs)) 115 | 116 | rule, fn = self.preprocess(rule, func) 117 | 118 | if self.rules.has_key(lhs): 119 | self.rules[lhs].append(rule) 120 | else: 121 | self.rules[lhs] = [ rule ] 122 | self.rule2func[rule] = fn 123 | self.rule2name[rule] = func.__name__[2:] 124 | self.ruleschanged = 1 125 | 126 | def collectRules(self): 127 | for name in _namelist(self): 128 | if name[:2] == 'p_': 129 | func = getattr(self, name) 130 | doc = func.__doc__ 131 | self.addRule(doc, func) 132 | 133 | def augment(self, start): 134 | # 135 | # Tempting though it is, this isn't made into a call 136 | # to self.addRule() because the start rule shouldn't 137 | # be subject to preprocessing. 138 | # 139 | startRule = (self._START, ( start, self._EOF )) 140 | self.rule2func[startRule] = lambda args: args[0] 141 | self.rules[self._START] = [ startRule ] 142 | self.rule2name[startRule] = '' 143 | return startRule 144 | 145 | def makeFIRST(self): 146 | union = {} 147 | self.first = {} 148 | 149 | for rulelist in self.rules.values(): 150 | for lhs, rhs in rulelist: 151 | if not self.first.has_key(lhs): 152 | self.first[lhs] = {} 153 | 154 | if len(rhs) == 0: 155 | self.first[lhs][None] = 1 156 | continue 157 | 158 | sym = rhs[0] 159 | if not self.rules.has_key(sym): 160 | self.first[lhs][sym] = 1 161 | else: 162 | union[(sym, lhs)] = 1 163 | changes = 1 164 | while changes: 165 | changes = 0 166 | for src, dest in union.keys(): 167 | destlen = len(self.first[dest]) 168 | self.first[dest].update(self.first[src]) 169 | if len(self.first[dest]) != destlen: 170 | changes = 1 171 | 172 | # 173 | # An Earley parser, as per J. Earley, "An Efficient Context-Free 174 | # Parsing Algorithm", CACM 13(2), pp. 94-102. Also J. C. Earley, 175 | # "An Efficient Context-Free Parsing Algorithm", Ph.D. thesis, 176 | # Carnegie-Mellon University, August 1968, p. 27. 177 | # 178 | 179 | def typestring(self, token): 180 | return None 181 | 182 | def error(self, token): 183 | print "Syntax error at or near `%s' token" % token 184 | raise SystemExit 185 | 186 | def parse(self, tokens): 187 | tree = {} 188 | tokens.append(self._EOF) 189 | states = { 0: [ (self.startRule, 0, 0) ] } 190 | 191 | if self.ruleschanged: 192 | self.makeFIRST() 193 | 194 | for i in xrange(len(tokens)): 195 | states[i+1] = [] 196 | 197 | if states[i] == []: 198 | break 199 | self.buildState(tokens[i], states, i, tree) 200 | 201 | #_dump(tokens, states) 202 | 203 | if i < len(tokens)-1 or states[i+1] != [(self.startRule, 2, 0)]: 204 | del tokens[-1] 205 | self.error(tokens[i-1]) 206 | rv = self.buildTree(tokens, tree, ((self.startRule, 2, 0), i+1)) 207 | del tokens[-1] 208 | return rv 209 | 210 | def buildState(self, token, states, i, tree): 211 | needsCompletion = {} 212 | state = states[i] 213 | predicted = {} 214 | #print state 215 | #print token 216 | for item in state: 217 | rule, pos, parent = item 218 | lhs, rhs = rule 219 | 220 | # 221 | # A -> a . (completer) 222 | # 223 | if pos == len(rhs): 224 | if len(rhs) == 0: 225 | needsCompletion[lhs] = (item, i) 226 | 227 | for pitem in states[parent]: 228 | if pitem is item: 229 | break 230 | 231 | prule, ppos, pparent = pitem 232 | plhs, prhs = prule 233 | 234 | if prhs[ppos:ppos+1] == (lhs,): 235 | new = (prule, 236 | ppos+1, 237 | pparent) 238 | if new not in state: 239 | state.append(new) 240 | tree[(new, i)] = [(item, i)] 241 | else: 242 | tree[(new, i)].append((item, i)) 243 | continue 244 | 245 | nextSym = rhs[pos] 246 | 247 | # 248 | # A -> a . B (predictor) 249 | # 250 | if self.rules.has_key(nextSym): 251 | # 252 | # Work on completer step some more; for rules 253 | # with empty RHS, the "parent state" is the 254 | # current state we're adding Earley items to, 255 | # so the Earley items the completer step needs 256 | # may not all be present when it runs. 257 | # 258 | if needsCompletion.has_key(nextSym): 259 | new = (rule, pos+1, parent) 260 | olditem_i = needsCompletion[nextSym] 261 | if new not in state: 262 | state.append(new) 263 | tree[(new, i)] = [olditem_i] 264 | else: 265 | tree[(new, i)].append(olditem_i) 266 | 267 | # 268 | # Has this been predicted already? 269 | # 270 | if predicted.has_key(nextSym): 271 | continue 272 | predicted[nextSym] = 1 273 | 274 | ttype = token is not self._EOF and \ 275 | self.typestring(token) or \ 276 | None 277 | if ttype is not None: 278 | # 279 | # Even smarter predictor, when the 280 | # token's type is known. The code is 281 | # grungy, but runs pretty fast. Three 282 | # cases are looked for: rules with 283 | # empty RHS; first symbol on RHS is a 284 | # terminal; first symbol on RHS is a 285 | # nonterminal (and isn't nullable). 286 | # 287 | for prule in self.rules[nextSym]: 288 | new = (prule, 0, i) 289 | prhs = prule[1] 290 | if len(prhs) == 0: 291 | state.append(new) 292 | continue 293 | prhs0 = prhs[0] 294 | if not self.rules.has_key(prhs0): 295 | if prhs0 != ttype: 296 | continue 297 | else: 298 | state.append(new) 299 | continue 300 | first = self.first[prhs0] 301 | if not first.has_key(None) and \ 302 | not first.has_key(ttype): 303 | continue 304 | state.append(new) 305 | continue 306 | 307 | for prule in self.rules[nextSym]: 308 | # 309 | # Smarter predictor, as per Grune & 310 | # Jacobs' _Parsing Techniques_. Not 311 | # as good as FIRST sets though. 312 | # 313 | prhs = prule[1] 314 | if len(prhs) > 0 and \ 315 | not self.rules.has_key(prhs[0]) and \ 316 | token != prhs[0]: 317 | continue 318 | state.append((prule, 0, i)) 319 | 320 | # 321 | # A -> a . c (scanner) 322 | # 323 | elif token == nextSym: 324 | #assert new not in states[i+1] 325 | states[i+1].append((rule, pos+1, parent)) 326 | 327 | def buildTree(self, tokens, tree, root): 328 | stack = [] 329 | self.buildTree_r(stack, tokens, -1, tree, root) 330 | return stack[0] 331 | 332 | def buildTree_r(self, stack, tokens, tokpos, tree, root): 333 | (rule, pos, parent), state = root 334 | 335 | while pos > 0: 336 | want = ((rule, pos, parent), state) 337 | 338 | if not tree.has_key(want): 339 | # 340 | # Since pos > 0, it didn't come from closure, 341 | # and if it isn't in tree[], then there must 342 | # be a terminal symbol to the left of the dot. 343 | # (It must be from a "scanner" step.) 344 | # 345 | pos = pos - 1 346 | state = state - 1 347 | stack.insert(0, tokens[tokpos]) 348 | tokpos = tokpos - 1 349 | else: 350 | # 351 | # There's a NT to the left of the dot. 352 | # Follow the tree pointer recursively (>1 353 | # tree pointers from it indicates ambiguity). 354 | # Since the item must have come about from a 355 | # "completer" step, the state where the item 356 | # came from must be the parent state of the 357 | # item the tree pointer points to. 358 | # 359 | children = tree[want] 360 | if len(children) > 1: 361 | child = self.ambiguity(children) 362 | else: 363 | child = children[0] 364 | tokpos = self.buildTree_r(stack, 365 | tokens, tokpos, 366 | tree, child) 367 | pos = pos - 1 368 | (crule, cpos, cparent), cstate = child 369 | state = cparent 370 | 371 | lhs, rhs = rule 372 | result = self.rule2func[rule](stack[:len(rhs)]) 373 | stack[:len(rhs)] = [result] 374 | return tokpos 375 | 376 | def ambiguity(self, children): 377 | # 378 | # XXX - problem here and in collectRules() if the same 379 | # rule appears in >1 method. But in that case the 380 | # user probably gets what they deserve :-) Also 381 | # undefined results if rules causing the ambiguity 382 | # appear in the same method. 383 | # 384 | sortlist = [] 385 | name2index = {} 386 | for i in range(len(children)): 387 | ((rule, pos, parent), index) = children[i] 388 | lhs, rhs = rule 389 | name = self.rule2name[rule] 390 | sortlist.append((len(rhs), name)) 391 | name2index[name] = i 392 | sortlist.sort() 393 | list = map(lambda (a,b): b, sortlist) 394 | return children[name2index[self.resolve(list)]] 395 | 396 | def resolve(self, list): 397 | # 398 | # Resolve ambiguity in favor of the shortest RHS. 399 | # Since we walk the tree from the top down, this 400 | # should effectively resolve in favor of a "shift". 401 | # 402 | return list[0] 403 | 404 | # 405 | # GenericASTBuilder automagically constructs a concrete/abstract syntax tree 406 | # for a given input. The extra argument is a class (not an instance!) 407 | # which supports the "__setslice__" and "__len__" methods. 408 | # 409 | # XXX - silently overrides any user code in methods. 410 | # 411 | 412 | class GenericASTBuilder(GenericParser): 413 | def __init__(self, AST, start): 414 | GenericParser.__init__(self, start) 415 | self.AST = AST 416 | 417 | def preprocess(self, rule, func): 418 | rebind = lambda lhs, self=self: \ 419 | lambda args, lhs=lhs, self=self: \ 420 | self.buildASTNode(args, lhs) 421 | lhs, rhs = rule 422 | return rule, rebind(lhs) 423 | 424 | def buildASTNode(self, args, lhs): 425 | children = [] 426 | for arg in args: 427 | if isinstance(arg, self.AST): 428 | children.append(arg) 429 | else: 430 | children.append(self.terminal(arg)) 431 | return self.nonterminal(lhs, children) 432 | 433 | def terminal(self, token): return token 434 | 435 | def nonterminal(self, type, args): 436 | rv = self.AST(type) 437 | rv[:len(args)] = args 438 | return rv 439 | 440 | # 441 | # GenericASTTraversal is a Visitor pattern according to Design Patterns. For 442 | # each node it attempts to invoke the method n_, falling 443 | # back onto the default() method if the n_* can't be found. The preorder 444 | # traversal also looks for an exit hook named n__exit (no default 445 | # routine is called if it's not found). To prematurely halt traversal 446 | # of a subtree, call the prune() method -- this only makes sense for a 447 | # preorder traversal. Node type is determined via the typestring() method. 448 | # 449 | 450 | class GenericASTTraversalPruningException: 451 | pass 452 | 453 | class GenericASTTraversal: 454 | def __init__(self, ast): 455 | self.ast = ast 456 | 457 | def typestring(self, node): 458 | return node.type 459 | 460 | def prune(self): 461 | raise GenericASTTraversalPruningException 462 | 463 | def preorder(self, node=None): 464 | if node is None: 465 | node = self.ast 466 | 467 | try: 468 | name = 'n_' + self.typestring(node) 469 | if hasattr(self, name): 470 | func = getattr(self, name) 471 | #print("Calling "+name) 472 | func(node) 473 | else: 474 | self.default(node) 475 | except GenericASTTraversalPruningException: 476 | return 477 | 478 | for kid in node: 479 | self.preorder(kid) 480 | 481 | name = name + '_exit' 482 | if hasattr(self, name): 483 | func = getattr(self, name) 484 | func(node) 485 | 486 | def postorder(self, node=None): 487 | if node is None: 488 | node = self.ast 489 | 490 | for kid in node: 491 | self.postorder(kid) 492 | 493 | name = 'n_' + self.typestring(node) 494 | if hasattr(self, name): 495 | func = getattr(self, name) 496 | func(node) 497 | else: 498 | self.default(node) 499 | 500 | 501 | def default(self, node): 502 | pass 503 | 504 | # 505 | # GenericASTMatcher. AST nodes must have "__getitem__" and "__cmp__" 506 | # implemented. 507 | # 508 | # XXX - makes assumptions about how GenericParser walks the parse tree. 509 | # 510 | 511 | class GenericASTMatcher(GenericParser): 512 | def __init__(self, start, ast): 513 | GenericParser.__init__(self, start) 514 | self.ast = ast 515 | 516 | def preprocess(self, rule, func): 517 | rebind = lambda func, self=self: \ 518 | lambda args, func=func, self=self: \ 519 | self.foundMatch(args, func) 520 | lhs, rhs = rule 521 | rhslist = list(rhs) 522 | rhslist.reverse() 523 | 524 | return (lhs, tuple(rhslist)), rebind(func) 525 | 526 | def foundMatch(self, args, func): 527 | func(args[-1]) 528 | return args[-1] 529 | 530 | def match_r(self, node): 531 | self.input.insert(0, node) 532 | children = 0 533 | 534 | for child in node: 535 | if children == 0: 536 | self.input.insert(0, '(') 537 | children = children + 1 538 | self.match_r(child) 539 | 540 | if children > 0: 541 | self.input.insert(0, ')') 542 | 543 | def match(self, ast=None): 544 | if ast is None: 545 | ast = self.ast 546 | self.input = [] 547 | 548 | self.match_r(ast) 549 | self.parse(self.input) 550 | 551 | def resolve(self, list): 552 | # 553 | # Resolve ambiguity in favor of the longest RHS. 554 | # 555 | return list[-1] 556 | 557 | def _dump(tokens, states): 558 | for i in range(len(states)): 559 | print 'state', i 560 | for (lhs, rhs), pos, parent in states[i]: 561 | print '\t', lhs, '::=', 562 | print string.join(rhs[:pos]), 563 | print '.', 564 | print string.join(rhs[pos:]), 565 | print ',', parent 566 | if i < len(tokens): 567 | print 568 | print 'token', str(tokens[i]) 569 | print 570 | --------------------------------------------------------------------------------