[zopeorg-checkins] CVS: Products/PythonMethod/zbytecodehacks - ChangeLog:1.1 Makefile:1.1 README:1.1 TODO:1.1 VSExec.py:1.1 __init__.py:1.1 attr_freeze.py:1.1 closure.py:1.1 code_editor.py:1.1 common.py:1.1 cyclehandle.py:1.1 dbc.py:1.1 find_function_call.py:1.1 iif.py:1.1 inline.py:1.1 label.py:1.1 macro.py:1.1 macros.py:1.1 opbases.py:1.1 ops.py:1.1 rationalize.py:1.1 tailr.py:1.1 version:1.1 xapply.py:1.1

Sidnei da Silva sidnei at x3ng.com.br
Fri May 30 11:17:50 EDT 2003


Update of /cvs-zopeorg/Products/PythonMethod/zbytecodehacks
In directory cvs.zope.org:/tmp/cvs-serv19195/PythonMethod/zbytecodehacks

Added Files:
	ChangeLog Makefile README TODO VSExec.py __init__.py 
	attr_freeze.py closure.py code_editor.py common.py 
	cyclehandle.py dbc.py find_function_call.py iif.py inline.py 
	label.py macro.py macros.py opbases.py ops.py rationalize.py 
	tailr.py version xapply.py 
Log Message:
Adding products needed for migration of NZO

=== Added File Products/PythonMethod/zbytecodehacks/ChangeLog ===
1999-12-11  Evan Simpson  <evan at 4-am.com>

	* Tupleizing and UntupleFunction now handle nested function definitions
	* Subdirectories reverted to v0.5 bytecodehacks, since the only change
	I've ever made was to accidentally change line endings in them to CRLF

1999-09-09  Evan Simpson  <evan at 4-am.com>

	* Tupleizing a function now omits globals

	* New UntupleFunction re-applies globals, with automatic initialization
	of variables to None, since there's no way to check if they exist.
        It also includes $functions, and checks __builtins__ handling.

	* Moved all bytecode manipulation into Munge_window class, which uses
        op.execute to maintain information about who did what to the stack.

	* Added Munger which re-enables creation of dictionary literals.

	* Made all Mungers load frequently used functions from the global dict
	into the local instead of storing them in co_consts.

	* Simplified GuardedOps and turned off test Guard.

	* Wrote lots of docstring.

1999-08-28  Evan Simpson  <evan at 4-am.com>

	* Ripped out Fleshy acquisition-style class and added
	CycleHandle in an attempt to improve speed.

	* code_editor.py: Added "as_tuple" to Function and EditableCode
	to provide (hopefully) pickleable representations.  Their __init__s
	now accept these tuples.

	* Added VSExec.py (the point of all this), which provides facilities
	for compiling arbitrary blocks of code with heavy restrictions.

1999-06-11  Michael Hudson  <mwh21 at cam.ac.uk>

	* a monumental amount has changed. I haven't been keeping the
	ChangeLog up to date, sorry.

1999-05-16  Michael Hudson  <mwh21 at cam.ac.uk>

	* doc/bch.tex: documented macro and macros.

	* macros.py: added basic library of macros. 

	* setq2.py: does same job as setq.py, but uses the new macro
	package.

	* macro.py It's a macro packages of sorts. Needs documentation.

1999-05-15  Michael Hudson  <mwh21 at cam.ac.uk>

	* inline.py: Substantially rewritten to use find_function_call,
	and to support keyword arguments. No varags yet.

	* setq.py Added changes written by Christian Tismer (now converts
	globals to locals)

1999-05-13  Michael Hudson  <mwh21 at cam.ac.uk>

	* Release 0.11 - cleaned up production of documentation following
	advice from the documentation master, Fred L. Drake.

1999-05-12  Michael Hudson  <mwh21 at cam.ac.uk>

	* Release 0.10.

	* doc/ There's documentation (gasp)

1999-05-10  Michael Hudson  <mwh21 at cam.ac.uk>

	* inline.py: Python now has inline functions! Bet you never
	expected that.

	* It's all changing again! Much polish, some docstrings,
	everything rewritten to use code_editor that wasn't already, many
	style fixes.

1999-05-06  Michael Hudson  <mwh21 at cam.ac.uk>

	* attr_freeze.py: implement an attribute freezer that works.

	* xapply2.py: implement xapply for functions (again!) using the
	new code editing framework from code_editor.py.

	* code_editor.py: That's more like it!

1999-05-04  Michael Hudson  <mwh21 at cam.ac.uk>

	* attr_freeze.py: implements a (buggy) attempt at freezing
	attribute references.

	* read_code.py,opbases.py,ops.py,write_ops.py,
	common.py,__init__.py: Much stuff added/changed. It not pretty or
	internally consistent yet. I might bash on it some more some
	time. I'm afraid I don't feel like explaining myself properly yet
	either.
	

1999-05-02  Michael Hudson  <mwh21 at cam.ac.uk>

	* README,ChangeLog: Added.

	* xapply.py: Added, following prompting by Christian Tismer.

	* closure.py: Made improvements suggested by Tim Peters.
	



=== Added File Products/PythonMethod/zbytecodehacks/Makefile ===
SUBDIRS=tests code_gen doc

clean: clean-local clean-recursive
release: version src-release doc-release

clean-local:
	$(RM) *.pyc *~ *.pyo

clean-recursive:
	for i in $(SUBDIRS); do \
	 (cd $$i && make clean); \
        done

src-release: clean
	cd .. && ./mkdist.sh

doc-release: clean
	cd doc && make release


=== Added File Products/PythonMethod/zbytecodehacks/README ===
Welcome to the bytecodehacks!

There's docmentation in doc/; To build it you need an unpacked python
source distribution somewhere, and for html output you also need
latex2html. 

Build docs like this:

$(path to Python source)/Doc/tools/mkhowto --$(format) (--a4) bch.tex

You can get built html docs at 

ftp://starship.python.net/pub/crew/mwh/bytecodehacks-doc-$(VERSION).tar.gz.

The bytecodehacks rewrite the bytecode of functions to do unlikely
things in Python.

The package (and that's how it's distributed) splits into two parts -
the byte code editing routines and the "bytecodehacks" that are
usuable without a degree in python arcanery, although one might help
understand some of the consequences.

Some highlights:

bytecodehacks.closure - bind global references to constants
bytecodehacks.xapply - a sort-of lazy apply
bytecodehacks.setq - this one should interest people!
bytecodehacks.inline - Python gets inline functions
bytecodehacks.macro - Python gets semantic (hygenic) macros!

Please note that these modules are not really bullet-proof, more a
proof-of-concept than anything else.

The are also public domain; do what you like with them. If you find
bugs, or more imaginative uses for these techniques, I'd surely like
to know!

Thanks for taking an interest.


=== Added File Products/PythonMethod/zbytecodehacks/TODO ===
polish attr_freeze.


=== Added File Products/PythonMethod/zbytecodehacks/VSExec.py ===
'''Safe execution of arbitrary code through bytecode munging

CodeBlock is a base class for bytecode munging of code strings.  Derived
classes should override 'forbid' and 'Mungers'.  The class constructor
takes a function name, a parameter string, and a function body string. It
combines the name, parameters, and body into a function (indented one
space), compiles it, and then examines its bytecode.

Any bytecode whose opcode is a key in 'forbid' with a true value may not
appear in the compiled code.

Each object in 'Mungers' is called with the code window object (at start)
and the variable name information for the function.  Mungers can use this
opportunity to examine the code and variables, and perform setup operations.
The value returned from the call is discarded if false, otherwise it is
assumed to be a collection of bytecode-specific functions.

The code window object is passed over the bytecodes, maintaining a stack
of "responsible" opcodes.  At any given position, the size of the stack
should be the same as the size of the run-time stack would be when that
part of the code is executing, and the top of the stack would have been
put there by the "responsible" opcode.

At each position, the mungers are examined to see if they have a function
corresponding to the current opcode.  If so, the function is called with
the code window object.  The function can then use the code window object to
examine or change the bytecode.

Once all processing is done, and if there have not been any errors, the
compiled, edited function is converted into a pickleable tuple, which is
stored as attribute 't' of the resulting instance.  In order for this to
work, mungers must not place unpickleable objects into the list of constants.
In order for unpickling to be robust, they should not place any function
objects there either.  If a function must be provided for use in the
bytecode, it should be loaded from the global dictionary, preferably with an
'illegal' name.
'''

from string import replace, join
import ops, code_editor
from closure import bind

class Warning(Exception): pass
class ForbiddenOp(Exception): pass

general_special_globals = {}

class Munge_window:
    def __init__(self, code, use_stack):
        self.code = code
        self.opn = -1
        self.use_stack = use_stack
        if use_stack:
            self.stack = []
            self.last_empty_stack = None
    def insert_code(self, rcode, at = None):
        opn = self.opn
        # rcode is passed as a (callables, args) pair
        rcode = map(apply, rcode[0], rcode[1])
        if at is None:
            at = opn
        self.code[at:at]=rcode
        self.opn = opn + len(rcode)
        return rcode

    def set_code(self, rct = 1, rcode = ()):
        'Replace "rct" bytecodes at current position with "rcode"'
        opn = self.opn
        # rcode is passed as a (callables, args) pair
        if rcode:
            rcode = map(apply, rcode[0], rcode[1])
        self.code[opn:opn+rct] = rcode
        if self.use_stack:
            stack = self.stack
            for op in rcode:
                try:
                    op.execute(stack)
                except:
                    del stack[:]
                if not stack:
                    self.last_empty_stack = op
        self.opn = opn + len(rcode) - 1
    def advance(self):
        self.opn = opn = self.opn + 1
        code = self.code
        if opn < len(code):
            self.op = code.opcodes[opn]
            self.opname = self.op.__class__.__name__
            return 1
        elif opn == len(code):
            # Hack!
            self.op = None
            self.opname = 'finalize'
            return 1
    def do_op(self):
        if self.use_stack and self.op:
            op = self.op
            stack = self.stack
            try:
                op.execute(stack)
            except:
                del stack[:]
            if not stack:
                self.last_empty_stack = op
    def after_code_for(self, stack_idx):
        stack = self.stack
        if stack_idx < 0 and len(stack) + stack_idx < 0:
            whichop = self.last_empty_stack
            if whichop is None:
                return 0
        else:
            whichop = stack[stack_idx]
        return self.code.index(whichop)+1

    def assert_stack_size(self, size, ctxt):
        if self.use_stack and len(self.stack) != size:
            raise Warning, ('PythonMethod Bug: %s objects were on the stack '
            '%s when only %s should be.' % (len(self.stack), ctxt, size)) 

class CodeBlock:
    '''Compile a string containing Python code, with restrictions'''

    forbid = {}
    Mungers = ()
    globals = {'__builtins__': None}
    forbidden = "Forbidden operation %s at line %d"

    def __init__(self, name, paramstr, src):
        self.name = name
        self.src = src
        self.paramstr = paramstr
        self.f = None
        # Exec 'src' as a function body, indented one space, with empty globals
        # Waaa: triple-quoted strings will get indented too
        defns = {'__builtins__': None}
        defblk = 'def %s(%s):\n %s\n' % (name, self.paramstr
        , replace(self.src, '\n', '\n '))
        exec defblk in defns
        block = defns[name]
        # Crack open the resulting function and munge it.
        f = code_editor.Function(block)
        f.func_globals = self.globals
        self.warnings, self.errors = [], []
        self.munge_data = {}
        self.munge(f.func_code)

        if not self.errors: self.t = f.as_tuple()

    def munge(self, fc, depth=0):
        # Recurse into nested functions first
        for subcode in fc.subcodes:
            self.munge(subcode, depth+1)
        # Make the current recursion depth accessible
        self.depth = depth
	code = fc.co_code
        warnings, errors = self.warnings, self.errors
        # Initialize the Munge objects
	mungers = []
        window = Munge_window(code, 1)
	margs = (self, window, fc)
	for M in self.Mungers:
            try:
                mungers.append(apply(M, margs))
            except Exception, e:
                errors.append(e.__class__.__name__ + ', ' + str(e))
        # Try to collect all initialization errors before failing
        if errors:
            return
        # Mungers which only perform an initial pass should return false
        mungers = filter(None, mungers)
        line = 0 ; forbid = self.forbid
        while window.advance():
            op, opname = window.op, window.opname
            if isinstance(op, ops.SET_LINENO):
                line = op.arg
                window.do_op()
            elif op and forbid.get(op.op, 0):
                errors.append(self.forbidden % (opname, line))
                window.do_op() #???
            else:
                for m in mungers:
                    handler = getattr(m, opname, None)
                    if handler:
                        try:
                            # Return true to prevent further munging
                            handled = handler(window)
                        except ForbiddenOp:
                            errors.append(self.forbidden % (opname, line))
                        except Exception, e:
                            raise
                            errors.append(e.__class__.__name__ + ', ' + str(e))
                        else:
                            if not handled:
                                continue
                        break
                else:
                    window.do_op()
    def __call__(self, *args, **kargs):
        F = code_editor.Function(self.t)
        F.func_globals = self.globals
        self.__call__ = f = F.make_function()
        return apply(f, args, kargs)

def _print_handler(printlist, *txt):
    add = printlist.append
    if len(txt):
        if printlist and printlist[-1:] != ['\n']:
            add(' ')
        add(str(txt[0]))
    else:
        add('\n')
def _join_printed(printlist):
    return join(printlist, '')
_join_printed = bind(_join_printed, join=join, map=map, str=str)

general_special_globals['$print_handler'] = _print_handler
general_special_globals['$join_printed'] = _join_printed
general_special_globals['$printed'] = None

class Printing:
    '''Intercept print statements

    Print statements are either converted to no-ops, or replaced with
    calls to a handler.  The default handler _print_handler appends the
    intended output to a list.  'printed' is made into a reserved name
    which can only be used to read the result of _join_printed.

    _print_handler and _join_printed should be provided in the global
    dictionary as $print_handler and $join_printed'''
    lfnames = (('$printed',), ('$print_handler',), ('$join_printed',))
    print_prep = ( (ops.LOAD_FAST, ops.LOAD_FAST), (lfnames[1], lfnames[0]) )
    get_printed = ( (ops.LOAD_FAST, ops.LOAD_FAST, ops.CALL_FUNCTION),
                    (lfnames[2], lfnames[0], (1,)) )
    init_printed = ( (ops.BUILD_LIST, ops.STORE_GLOBAL), 
                      ((0,), lfnames[0]) )
    make_local = (ops.LOAD_GLOBAL, ops.STORE_FAST)
    call_print1 = ( (ops.CALL_FUNCTION, ops.POP_TOP), ((1,), ()) )
    call_print2 = ( (ops.CALL_FUNCTION, ops.POP_TOP), ((2,), ()) )
    
    def __init__(self, cb, w, fc):
        if 'printed' in fc.co_varnames:
            raise SyntaxError, '"printed" is a reserved word.'
        self.warnings, self.depth = cb.warnings, cb.depth
        self.md = cb.munge_data['Printing'] = cb.munge_data.get('Printing', {})
        self.names_used = names_used = {}
        names = fc.co_names
        if 'printed' in names:
            names_used[0] = names.index('printed')
            names_used[2] = 1
        else:
            self.LOAD_GLOBAL = None
    def finalize(self, w):
        names_used = self.names_used
        if names_used:
            # Load special names used by the function (and $printed always)
            ln = self.lfnames
            names_used[0] = 1
            for i in names_used.keys():
                w.insert_code( (self.make_local, (ln[i], ln[i])), 0)
            # Inform higher-level mungers of name usage
            self.md.update(names_used)
        if self.md and self.depth == 0:
            # Initialize the $printed variable in the base function
            w.insert_code(self.init_printed, 0)
            if not self.md.has_key(2):
                self.warnings.append(
                "Prints, but never reads 'printed' variable.")

    def PRINT_ITEM(self, w):
        # Load the printing function before the code for the operand.
        w.insert_code(self.print_prep, w.after_code_for(-2))
        w.assert_stack_size(1, "at a 'print' statement")
        # Instead of printing, call our function and discard the result.
        w.set_code(1, self.call_print2)
        self.names_used[1] = 1
        return 1
    def PRINT_NEWLINE(self, w):
        w.assert_stack_size(0, "at a 'print' statement")
        w.insert_code(self.print_prep)
        w.set_code(1, self.call_print1)
        self.names_used[1] = 1
        return 1
    def LOAD_GLOBAL(self, w):
        if w.op.arg==self.names_used[0]:
            # Construct the print result instead of getting non-existent var.
            w.set_code(1, self.get_printed)
            return 1

def PublicNames(cb, w, fc):
    '''Restrict access to all but public names

    Forbid use of any multi-character name starting with _
    '''
    protected = []
    for name in fc.co_names:
        if name[:1]=='_' and len(name)>1:
            protected.append(name)
    if protected:
        raise SyntaxError, ('Names starting with "_" are not allowed (%s).'
                            % join(protected, ', '))

class AllowMapBuild:
    '''Allow literal mappings

    Construction of literal dictionaries requires STORE_SUBSCR, which is
    usually forbidden.  Allow it by checking the stack.'''
    def __init__(self, cb, w, fc):
        pass
    def STORE_SUBSCR(self, w):
        if isinstance(w.stack[-2], ops.BUILD_MAP):
            return
        raise ForbiddenOp

def _get_call(w):
    load_guard = ((ops.LOAD_FAST, ops.LOAD_ATTR), (('$guard',), (guard,)))
    # Load the binary guard function before its parameters are computed.
    iops = w.insert_code(load_guard, w.after_code_for(-3))
    # Fix the execution stack to refer to the loaded function.
    if w.use_stack: w.stack[-2:-2] = iops[1:]
    # Call guard function instead of performing binary op
    w.set_code(1, cf2)
    return 1
def _SLICE(w):
    load_guard = ((ops.LOAD_FAST, ops.LOAD_ATTR, ops.ROT_TWO),
                  (('$guard',), ('getslice',), ()))
    # Load the Slice guard and switch it with the argument.
    w.insert_code(load_guard, w.opn+1)
    # Call the slice guard after performing the slice.
    w.insert_code(cf1, w.opn+4)
    return 1

class _GuardedOps:
    def __call__(self, cb, w, fc):
        localize_guard = ( (ops.LOAD_GLOBAL, ops.STORE_FAST), (('$guard',),('$guard',)) )
        # Insert setup code; no need to fix stack
        w.insert_code(localize_guard, 0)
        return self

def GuardedOps(guards):
    '''Allow operations to be Guarded, by replacing them with guard calls.

    Construct a munging object which will replace specific opcodes with
    calls to methods of a guard object.  The guard object must appear in
    the global dictionary as $guard.'''
    gops = _GuardedOps()
    opmap = (('mul', 'BINARY_MULTIPLY')
            ,('div', 'BINARY_DIVIDE')
            ,('power', 'BINARY_POWER')
            ,('getattr', 'LOAD_ATTR')
            ,('getitem', 'BINARY_SUBSCR'))
    cf1 = ((ops.CALL_FUNCTION,), ((1,),))
    cf2 = ((ops.CALL_FUNCTION,), ((2,),))
    for guard, defname in opmap:
        if hasattr(guards, guard):
            setattr(gops, defname, bind(_get_call, guard=guard, cf2=cf2))
    if hasattr(guards, 'getslice'):
        gops.SLICE_3 = gops.SLICE_2 = gops.SLICE_1 = gops.SLICE_0 = bind(_SLICE, cf1=cf1)
    return gops

class SafeBlock(CodeBlock):
    forbid = {ops.STORE_FAST.op: 0, ops.STORE_GLOBAL.op: 0, ops.STORE_SUBSCR.op: 0}
    for opclass in ops._bytecodes.values():
        if forbid.get(opclass.op, 1):
            opname = opclass.__name__
            if (opname[:6]=='STORE_' or opname[:7]=='DELETE_' or opname[:5]=='EXEC_'):
                forbid[opclass.op] = 1
    Mungers = [Printing, PublicNames, AllowMapBuild]

def UntupleFunction(t, special_globals, **globals):
    import new
    globals.update(general_special_globals)
    globals.update(special_globals)
    globals['global_exists'] = defined = globals.has_key
    if not defined('__builtins__'):
        globals['__builtins__'] = {}

    t = list(t)
    # Handle nested functions and lambdas
    t_code = t[2]
    if len(t_code) == 13:
       sub_codes = [t_code]
       funstack = [sub_codes]
       while funstack:
         if len(t_code) == 13:
           # This has nested code objects, so make it mutable
           sub_codes[0] = t_parent = t_code = list(t_code)
           # Put the list of nested codes on the stack for processing
           sub_codes = list(t_code.pop())
           funstack.append(sub_codes)
         else:
           # This code tuple is fully processed, so untuple it
           func_code = apply(new.code, tuple(t_code))
           # Find the first placeholder () in the parent's constants
           t_consts = list(t_parent[5])
           # Replace the placeholder with the code object
           t_consts[t_consts.index(())] = func_code
           t_parent[5] = tuple(t_consts)
           # Clear it from the stack
           del sub_codes[0]
         # Get the next code tuple to process
         if not sub_codes:
           # Back up one level
           funstack.pop()
           sub_codes = funstack[-1]
           if len(funstack) > 1:
               t_parent = funstack[-2][0]
           else:
               funstack = None
         t_code = sub_codes[0]
    f = new.function(apply(new.code, tuple(t_code)), globals, t[0])
    f.func_defaults = t[3] and tuple(t[3])
    f.func_doc = t[1]
    return f

if 0:
    #demonstrator Guard
    class Guard:
        def mul(self, a, b):
            if a*b>10:
                return 'ha!'
            else:
                return a*b
        def getslice(self, s):
            if len(s)>2:
                raise "Too long a slice!"
            return s

def test(p, c):
    sb = SafeBlock('f', p, c)
    print sb.errors, sb.warnings
    f = code_editor.Function(sb.t)
    for c in f.func_code.co_code:
        print c
    for subcode in f.func_code.subcodes:
        for c in subcode.co_code:
            print ' ', c
    return sb

if __name__ == '__main__':    
	sb = test('x', '''\
print x
def plus1(x):
  print x+1
plus1(x)
return printed''')
        f = UntupleFunction(sb.t, {})
	#from dis import dis
	#dis(f)
	print f(2),
	print f(3),



=== Added File Products/PythonMethod/zbytecodehacks/__init__.py ===
__all__=[
    'closure',
    'xapply',
    'common',
    'inline',
    'code_editor',
    'opbases',
    'ops',
    'attr_freeze',
    'code_gen']


=== Added File Products/PythonMethod/zbytecodehacks/attr_freeze.py ===
from code_editor import Function
from ops import LOAD_GLOBAL, LOAD_ATTR, LOAD_CONST

def freeze_one_attr(cs,code,attr,value):
    looking_for=0
    is_global=1
    inserted=0

    i = 0
    while i < len(cs):
        op=cs[i]
        if is_global:
            if op.__class__ is LOAD_GLOBAL:
                if code.co_names[op.arg]==attr[looking_for]:
                    looking_for=looking_for+1
                    is_global=0
        else:
            if op.__class__ is LOAD_ATTR \
               and code.co_names[op.arg]==attr[looking_for]:
                looking_for=looking_for+1
                if looking_for == len(attr):
                    inserted=1
                    newop=LOAD_CONST(len(code.co_consts))
                    cs[i-len(attr)+1:i+1]=[newop]
                    i=i-len(attr)
                    looking_for=0
                    is_global=1
            else:
                looking_for=0
                is_global=1
        i=i+1

    if inserted:
        code.co_consts.append(value)
    return cs
    
class Ref:
    def __init__(self,name=()):
        self.name=name
    def __getattr__(self,attr):
        return Ref(self.name+(attr,))
    def __call__(self):
        return self.name
    def __repr__(self):
        return `self.name`

def freeze_attrs(func,*vars):
    func=Function(func)
    code=func.func_code
    cs=code.co_code

    if len(vars)%2 <> 0:
        raise TypeError, "wrong number of arguments"

    for i in range(0,len(vars),2):
        freeze_one_attr(cs,code,vars[i](),vars[i+1])

    return func.make_function()


=== Added File Products/PythonMethod/zbytecodehacks/closure.py ===
"""\
closure

implements a form of closures by abusing the co_consts field of a code
object.

exports: bind, bind_locals, bind_now
and contains two examples: make_adder, make_balance
"""

from code_editor import Function
from ops import *

def scan_for_STORE(func,name):
    for i in func.func_code.co_code:
        if i.__class__ in [STORE_FAST,STORE_NAME,STORE_GLOBAL] \
           and i.name == name:
            return 1
    return 0

def bind(function,newname=None,**vars):
    """\
bind(function[,newname],var1=value1,var2=value2,...) -> function

returns a new function (optionally renamed) where every reference to
one of var1, var2, etc is replaced by a reference to the respective
valueN."""
    func = Function(function)
    code = func.func_code
    cs   = func.func_code.co_code

    name2index = {}

    mutated = {}

    for name in vars.keys():
        mutated[name] = scan_for_STORE(func,name)

    if 0 in code.co_consts:
        zeroIndex = code.co_consts.index(0)
    else:
        zeroIndex = len(code.co_consts)
        code.co_consts.append(0)

    i = 0

    while i < len(cs):
        op = cs[i]
        i = i + 1
        # should LOAD_NAME be here??? tricky, I'd say
        if op.__class__ in [LOAD_GLOBAL,LOAD_NAME,LOAD_FAST]:
            if not vars.has_key(op.name):
                continue
            if mutated[name]:
                if not name2index.has_key(op.name):
                    name2index[op.name]=len(code.co_consts)
                code.co_consts.append([vars[op.name]])
                cs[i-1:i] = [LOAD_CONST(name2index[op.name]),
                             LOAD_CONST(zeroIndex),
                             BINARY_SUBSCR()]
                i = i + 2
            else:
                if not name2index.has_key(op.name):
                    name2index[op.name]=len(code.co_consts)
                code.co_consts.append(vars[op.name])
                cs[i-1] = LOAD_CONST(name2index[op.name])
        elif op.__class__ in [STORE_FAST,STORE_NAME,STORE_GLOBAL]:
            if not vars.has_key(op.name):
                continue
            if not mutated[name]:
                continue # shouldn't be reached
            cs[i-1:i] = [LOAD_CONST(name2index[op.name]),
                         LOAD_CONST(zeroIndex),
                         STORE_SUBSCR()]
            i = i + 2

    if newname is not None:
        func.func_name = newname

    return func.make_function()

bind=Function(bind)
bind.func_code.co_varnames[0]='$function'
bind.func_code.co_varnames[1]='$newname'
bind=bind.make_function()

def bind_locals(func):
    """bind_locals(func) -> function

returns a new function where every global variable reference in func
is replaced, if possible, by a reference to a local variable in the
callers context."""
    try:
        raise ""
    except:
        import sys
        frame = sys.exc_traceback.tb_frame.f_back
        name = func.func_name+'+'
        l = apply(bind,(func,name),frame.f_locals)
        frame = None
        return l

def bind_now(func):
    """bind_now(func) -> function

returns a new function where every global variable reference in func
is replaced, if possible, by a reference to a variable in the callers
context."""
    try:
        raise ""
    except:
        import sys
        frame = sys.exc_traceback.tb_frame.f_back
        l = apply(bind,(func,),frame.f_locals)
        g = apply(bind,(l,),frame.f_globals)
        frame = None
        return g

## examples

def make_adder(n):
    """make_adder(n) -> function

return a monadic function that adds n to its argument."""
    def adder(x):
        return x+n
    return bind_locals(adder)

def make_balance(initial_amount):
    """make_balance(initial_amount) -> function

demonstrates an object with state, sicp style."""
    def withdraw(amount):
        if current[0]<amount:
            raise "debt!"
        else:
            current[0]=current[0]-amount
        return current[0]
    return bind(withdraw,current=[initial_amount])


=== Added File Products/PythonMethod/zbytecodehacks/code_editor.py ===
# the third attempt; maybe it'll work sometime.

# interface I want:
# mc=MutableCode(<code object>)
# behaves like list of opcodes, eg
# len(mc) => number of bytecodes in code
# mc[i] returns some representation of the ith opcode
# mc.assemble() => codestring, or maybe code object.

import types,StringIO,struct,new

import ops
from cyclehandle import CycleHandle

class CodeString(CycleHandle):
    def __init__(self,cs=None,bytecodes=None):
	self._set_workers(CodeStringWorker(cs, bytecodes))
        
class CodeStringWorker:
    def __init__(self,cs,bytecodes):
        self.labels=[]
        self.byte2op={}
        self.opcodes=[]

        if bytecodes is None:
            bytecodes = ops._bytecodes
        
        if type(cs) is type(""):
            self.disassemble_no_code(cs,bytecodes)
        else:
            self.disassemble(cs,bytecodes)
    def disassemble(self,code,bytecodes):
        self.labels = []
        self.byte2op = {}
        self.opcodes = []
        self.code = code

        cs=StringIO.StringIO(code.co_code)
        i, op, n = 0, 0, len(code.co_code)
        
        while i < n:
            self.byte2op[i]=op
            byte=cs.read(1)
            self.opcodes.append(bytecodes[byte](cs,self))
            i = cs.tell()
            op = op + 1

        del self.code

        for label in self.labels:
            label.resolve(self)
    def disassemble_no_code(self,codestring,bytecodes):
        self.labels = []
        self.byte2op = {}
        self.opcodes = []

        cs=StringIO.StringIO(codestring)
        i, op, n = 0, 0, len(codestring)
        
        while i < n:
            self.byte2op[i]=op
            byte=cs.read(1)
            self.opcodes.append(bytecodes[byte](cs,self))
            i = cs.tell()
            op = op + 1

        for label in self.labels:
            label.resolve(self)            
    def add_label(self,label):
        self.labels.append(label)
    def find_labels(self,index):
        labelled=self.opcodes[index]
        pointees=[]
        for l in self.labels:
            if l.op == labelled:
                pointees.append(l)
        return pointees
    def __getitem__(self,index):
        return self.opcodes[index]
    def __setitem__(self,index,value):
        # find labels that point to the removed opcode
        pointees=self.find_labels(index)
        if self.opcodes[index].is_jump():
            self.labels.remove(self.opcodes[index].label)
        self.opcodes[index]=value
        for l in pointees:
            l.op=value
        if value.is_jump():
            self.labels.append(value.label)
    def __delitem__(self,index):
        # labels that pointed to the deleted item get attached to the
        # following insn (unless it's the last insn - in which case I
        # don't know what you're playing at, but I'll just point the
        # label at what becomes the last insn)
        pointees=self.find_labels(index)
        if index + 1 == len(self.opcodes):
            replacement = self.opcodes[index]
        else:
            replacement = self.opcodes[index + 1]
        for l in pointees:
            l.op=replacement
        going=self.opcodes[index]
        if going.is_jump():
            self.labels.remove(going.label)
        del self.opcodes[index]
    def __getslice__(self,lo,hi):
        return self.opcodes[lo:hi]
    def __setslice__(self,lo,hi,values):
        # things that point into the block being stomped on get
        # attached to the start of the new block (if there are labels
        # pointing into the block, rather than at its start, a warning
        # is printed, 'cause that's a bit dodgy)
        pointees=[]
        opcodes = self.opcodes
        indices=range(len(opcodes))[lo:hi]
        for i in indices:
            if opcodes[i].is_jump():
                self.labels.remove(opcodes[i].label)
            p=self.find_labels(i)
            if p and i <> lo:
                print "What're you playing at?"
            pointees.extend(p)
        codes = []
        for value in values:
            if value.is_jump():
                self.labels.append(value.label)
            codes.append(value)
        opcodes[lo:hi]=codes
        replacement = opcodes[min(lo, len(opcodes)-1)]
        for l in pointees:
            l.op = replacement
    def __delslice__(self,lo,hi):
        self.__setslice__(lo, hi, [])
    def __len__(self):
        return len(self.opcodes)
    def append(self,value):
        self.opcodes.append(value)
        if value.is_jump():
            self.labels.append(value.label)
    def insert(self,index,value):
        self.opcodes.insert(index,value)     
        if value.is_jump():
            self.labels.append(value.label)
    def remove(self,x):
        del self[self.opcodes.index(x)]
    def index(self,x):
        return self.opcodes.index(x)
    def assemble(self):
        out=StringIO.StringIO()
        for i in self:
            i.byte=out.tell()
            out.write(i.assemble(self))
        for l in self.labels:
            l.write_refs(out)
        out.seek(0)
        return out.read()
    def set_code(self, code):
        self.code = code

class EditableCode(CycleHandle):
    def __init__(self,code=None):
	self._set_workers(EditableCodeWorker(code))    

class EditableCodeWorker:
    # bits for co_flags
    CO_OPTIMIZED   = 0x0001
    CO_NEWLOCALS   = 0x0002
    CO_VARARGS     = 0x0004
    CO_VARKEYWORDS = 0x0008

    AUTO_RATIONALIZE = 0
    
    def __init__(self,code=None):
        if code is None:
            self.init_defaults()
        elif type(code) in (type(()), type([])):
            self.init_tuple(code)
        else:
            self.init_code(code)
        self.co_code.set_code(self)
    def name_index(self,name):
        if name not in self.co_names:
            self.co_names.append(name)
        return self.co_names.index(name)
    def local_index(self,name):
        if name not in self.co_varnames:
            self.co_varnames.append(name)
        return self.co_varnames.index(name)
    def rationalize(self):
        from rationalize import rationalize
        rationalize(self)
    def init_defaults(self):
        self.co_argcount = 0
        self.co_stacksize = 0 # ???
        self.co_flags = 0 # ???
        self.co_consts = []
        self.co_names = []
        self.co_varnames = []
        self.co_filename = '<edited code>'
        self.co_name = 'no name'
        self.co_firstlineno = 0
        self.co_lnotab = '' # ???
        self.co_code = CodeString()
        self.subcodes = []
    def init_code(self,code):
        self.co_argcount = code.co_argcount
        self.co_stacksize = code.co_stacksize
        self.co_flags = code.co_flags
        self.co_consts = consts = list(code.co_consts)
        self.co_names = list(code.co_names)
        self.co_varnames = list(code.co_varnames)
        self.co_filename = code.co_filename
        self.co_name = code.co_name
        self.co_firstlineno = code.co_firstlineno
        self.co_lnotab = code.co_lnotab
        self.co_code = CodeString(code)
        self.subcodes = subcodes = []
        from types import CodeType
        for i in range(len(consts)):
            if type(consts[i]) == CodeType:
                subcodes.append(EditableCode(consts[i]))
                consts[i] = ()
    def init_tuple(self,tup):
        self.subcodes = []
        if len(tup)==13:
            self.subcodes = map(EditableCode, tup[-1])
            tup = tup[:-1]
        ( self.co_argcount, ignored, self.co_stacksize, self.co_flags
        , self.co_code, co_consts, co_names, co_varnames, self.co_filename
        , self.co_name, self.co_firstlineno, self.co_lnotab
        ) = tup
        self.co_consts = list(co_consts)
        self.co_names = list(co_names)
        self.co_varnames = list(co_varnames)
        self.co_code = CodeString(self)
    def make_code(self):
        if self.AUTO_RATIONALIZE:
            self.rationalize()
        else:
            # hack to deal with un-arg-ed names
            for op in self.co_code:
                if (op.has_name() or op.has_local()) and not hasattr(op, "arg"):
                    if op.has_name():
                      op.arg = self.name_index(op.name)
                    else:
                      op.arg = self.local_index(op.name)

        return apply(new.code, self.as_tuple()[:12])
    def set_function(self, function):
        self.function = function
    def as_tuple(self):
        # the assembling might change co_names or co_varnames - so
        # make sure it's done *before* we start gathering them.
        bytecode = self.co_code.assemble()
        subcodes = []
        for subcode in self.subcodes:
            subcodes.append(subcode.as_tuple())
        return (
            self.co_argcount,
            len(self.co_varnames),
            self.co_stacksize,
            self.co_flags,
            bytecode,
            tuple(self.co_consts),
            tuple(self.co_names),
            tuple(self.co_varnames),
            self.co_filename,
            self.co_name,
            self.co_firstlineno,
            self.co_lnotab,
            tuple(subcodes))

class Function(CycleHandle):
    def __init__(self,func=None):
	self._set_workers(FunctionWorker(func))
	    
class FunctionWorker:
    def __init__(self,func):
        if func is None:
            self.init_defaults()
        elif type(func) in (type(()), type([])):
            self.init_tuple(func)
        else:
            self.init_func(func)
        self.func_code.set_function(self)
    def init_defaults(self):
        self.__name = "no name"
        self.__doc = None
        self.func_code = EditableCode()
        self.func_defaults = []
        self.func_globals = {} # ???
    def init_func(self,func):
        self.__name = func.func_name
        self.__doc = func.func_doc
        self.func_code = EditableCode(func.func_code)
        self.func_defaults = func.func_defaults
        self.func_globals = func.func_globals
    def init_tuple(self,tup):
        ( self.__name, self.__doc, func_code, self.func_defaults
        , self.func_globals
        ) = tup
        self.func_code = EditableCode(func_code)
    def __getattr__(self,attr):
        # for a function 'f.__name__ is f.func_name'
        # so lets hack around to support that...
        if attr in ['__name__','func_name']:
            return self.__name
        if attr in ['__doc__','func_doc']:
            return self.__doc
        raise AttributeError, attr
    def __setattr__(self,attr,value):
        if attr in ['__name__','func_name']:
            self.__name = value
        elif attr in ['__doc__','func_doc']:
            self.__doc = value
        else:
            self.__dict__[attr]=value
    def make_function(self):
        newfunc = new.function(
            self.func_code.make_code(),
            self.func_globals,
            self.__name)
        if not self.func_defaults:
            defs=None
        else:
            defs=tuple(self.func_defaults)
        newfunc.func_defaults = defs
        newfunc.func_doc = self.__doc
        return newfunc
    def __call__(self,*args,**kw):
        return apply(self.make_function(),args,kw)
    def set_method(self, method):
        self.method = method
    def as_tuple(self):
        if not self.func_defaults:
            defs=None
        else:
            defs=tuple(self.func_defaults)
        return (self.__name, self.__doc, self.func_code.as_tuple(), defs, {})
    
class InstanceMethod(CycleHandle):
    def __init__(self,meth=None):
        self._set_workers(InstanceMethodWorker(meth))

class InstanceMethodWorker:
    def __init__(self,meth):
        if meth is None:
            self.init_defaults()
        else:
            self.init_meth(meth)
        self.im_func.set_method(self)
    def init_defaults(self):
        self.im_class = None
        self.im_func = Function()
        self.im_self = None
    def init_meth(self,meth):
        self.im_class = meth.im_class
        self.im_func = Function(meth.im_func)
        self.im_self = meth.im_self
    def make_instance_method(self):
        return new.instancemethod(
            self.im_func.make_function(),
            self.im_self,self.im_class)

class FunctionOrMethod:
    def __init__(self,functionormethod):
        if type(functionormethod) is types.FunctionType:
            self.is_method = 0
            self.function = Function(functionormethod)
        elif type(functionormethod) is types.UnboundMethodType:
            self.is_method = 1
            self.method = InstanceMethod(functionormethod)
            self.function = self.method.im_func
    def make_function_or_method(self):
        if self.is_method:
            return self.method.make_instance_method()
        else:
            return self.function.make_function()
        


=== Added File Products/PythonMethod/zbytecodehacks/common.py ===
import new

def copy_code_with_changes(codeobject,
                           argcount=None,
                           nlocals=None,
                           stacksize=None,
                           flags=None,
                           code=None,
                           consts=None,
                           names=None,
                           varnames=None,
                           filename=None,
                           name=None,
                           firstlineno=None,
                           lnotab=None):
    if argcount    is None: argcount    = codeobject.co_argcount
    if nlocals     is None: nlocals     = codeobject.co_nlocals
    if stacksize   is None: stacksize   = codeobject.co_stacksize
    if flags       is None: flags       = codeobject.co_flags
    if code        is None: code        = codeobject.co_code
    if consts      is None: consts      = codeobject.co_consts
    if names       is None: names       = codeobject.co_names
    if varnames    is None: varnames    = codeobject.co_varnames
    if filename    is None: filename    = codeobject.co_filename
    if name        is None: name        = codeobject.co_name
    if firstlineno is None: firstlineno = codeobject.co_firstlineno
    if lnotab      is None: lnotab      = codeobject.co_lnotab
    return new.code(argcount,
                    nlocals,
                    stacksize,
                    flags,
                    code,
                    consts,
                    names,
                    varnames,
                    filename,
                    name,
                    firstlineno,
                    lnotab)

code_attrs=['argcount',
            'nlocals',
            'stacksize',
            'flags',
            'code',
            'consts',
            'names',
            'varnames',
            'filename',
            'name',
            'firstlineno',
            'lnotab']




=== Added File Products/PythonMethod/zbytecodehacks/cyclehandle.py ===
class CycleHandle:
    '''CycleHandles are proxies for cycle roots

A CycleHandle subclass should create one or more workers, and pass them
to _set_workers.  These workers can then participate in cycles, as long
as deleting all of the worker's attributes will break the cycle.  When a 
CycleHandle instance goes away, it deletes all attributes of all of
its workers.  You could also explicitly call drop_workers.

For example,
>>> class Ham:
...     def __del__(self):
...         print 'A ham has died!'
...            
>>> ct = CycleHandle()
>>> ct._set_workers(Ham(), Ham())
>>> ct._workers[0].ham2 = ct._workers[1]
>>> ct._workers[1].ham1 = ct._workers[0]
>>> del ct
A ham has died!
A ham has died!
'''
    _workers = ()
    def _set_workers(self, *workers):
        self.__dict__['_workers'] = workers
    def _not_mutable(self, *x):
        raise TypeError, 'CycleHandle is not mutable'
    _not_mutable_defs = ('__delslice__', '__setslice__', '__delitem__'
    , '__setitem__', '__delattr__', '__setattr__')
    def __getattr__(self, attr):
        for worker in self._workers:
            if hasattr(worker, attr):
                return getattr(worker, attr)
        if attr in self._not_mutable_defs:
            return self._not_mutable
        raise AttributeError, attr
    def _drop_workers(self):
        for worker in self._workers:
            worker.__dict__.clear()
        self.__dict__['_workers'] = ()
    def __del__(self, drop_workers=_drop_workers):
        drop_workers(self)

def _test():
    import doctest, cyclehandle
    return doctest.testmod(cyclehandle)

if __name__ == "__main__":
    _test()


=== Added File Products/PythonMethod/zbytecodehacks/dbc.py ===
from code_editor import Function
from ops import *
import dis,new,string

PRECONDITIONS  = 1
POSTCONDITIONS = 2
INVARIANTS     = 4
EVERYTHING     = PRECONDITIONS|POSTCONDITIONS|INVARIANTS

if __debug__:
    __strength__ = PRECONDITIONS|POSTCONDITIONS
else:
    __strength__ = 0

# TODO: docs, sort out inheritance.

if __debug__:
    def add_contracts(target_class,contract_class,strength=None):
        if strength is None:
            strength = __strength__

        newmethods = {}
        contractmethods = contract_class.__dict__
        if strength & INVARIANTS:
            inv = contractmethods.get("class_invariants",None)

        for name, meth in target_class.__dict__.items():
            if strength & PRECONDITIONS:
                pre = contractmethods.get("pre_"+name,None)
                if pre is not None:
                    meth = add_precondition(meth,pre)
            if strength & POSTCONDITIONS:
                post = contractmethods.get("post_"+name,None)
                if post is not None:
                    meth = add_postcondition(meth,post)
            if (strength & INVARIANTS) and inv \
               and type(meth) is type(add_contracts):
                if name <> '__init__':
                    meth = add_precondition(meth,inv)
                meth = add_postcondition(meth,inv)
            newmethods[name] = meth

        return new.classobj(target_class.__name__,
                            target_class.__bases__,
                            newmethods)

    def add_precondition(meth,cond):
        meth  = Function(meth)
        cond  = Function(cond)

        mcs = meth.func_code.co_code
        ccs = cond.func_code.co_code

        nlocals = len(meth.func_code.co_varnames)
        nconsts = len(meth.func_code.co_consts)
        nnames  = len(meth.func_code.co_names)

        nargs   = meth.func_code.co_argcount

        retops = []

        for op in ccs:
            if op.__class__ is RETURN_VALUE:
                # RETURN_VALUEs have to be replaced by JUMP_FORWARDs
                newop = JUMP_FORWARD()
                ccs[ccs.index(op)] = newop
                retops.append(newop)
            elif op.op in dis.hasname:
                op.arg = op.arg + nnames
            elif op.op in dis.haslocal:
                if op.arg >= nargs:
                    op.arg = op.arg + nlocals
            elif op.op in dis.hasconst:
                op.arg = op.arg + nconsts

        new = POP_TOP()
        mcs.insert(0,new)
        mcs[0:0] = ccs.opcodes
        for op in retops:
            op.label.op = new

        meth.func_code.co_consts.extend(cond.func_code.co_consts)
        meth.func_code.co_varnames.extend(cond.func_code.co_varnames)
        meth.func_code.co_names.extend(cond.func_code.co_names)

        return meth.make_function()

    def add_postcondition(meth,cond):
        """ a bit of a monster! """
        meth  = Function(meth)
        cond  = Function(cond)

        mcode = meth.func_code
        ccode = cond.func_code

        mcs = mcode.co_code
        ccs = ccode.co_code

        nlocals = len(mcode.co_varnames)
        nconsts = len(mcode.co_consts)
        nnames  = len(mcode.co_names)

        nargs   = ccode.co_argcount

        cretops = []

        Result_index = len(meth.func_code.co_varnames)

        mcode.co_varnames.append('Result')

        old_refs = find_old_refs(cond)

        for op in ccs:
            if op.__class__ is RETURN_VALUE:
                newop = JUMP_FORWARD()
                ccs[ccs.index(op)] = newop
                cretops.append(newop)
            elif op.op in dis.hasname:
                if cond.func_code.co_names[op.arg] == 'Result' \
                   and op.__class__ is LOAD_GLOBAL:
                    ccs[ccs.index(op)] = LOAD_FAST(Result_index)
                else:
                    op.arg = op.arg + nnames
            elif op.op in dis.haslocal:
                if op.arg >= nargs:
                    op.arg = op.arg + nlocals + 1 # + 1 for Result
            elif op.op in dis.hasconst:
                op.arg = op.arg + nconsts

        # lets generate the prologue code to save values for `Old'
        # references and point the LOAD_FASTs inserted by
        # find_old_refs to the right locations.

        prologue = []

        for ref, load_op in old_refs:
            if ref[0] in mcode.co_varnames:
                prologue.append(LOAD_FAST(mcode.co_varnames.index(ref[0])))
            else:
                prologue.append(LOAD_GLOBAL(mcode.name_index(ref[0])))
            for name in ref[1:]:
                prologue.append(LOAD_ATTR(mcode.name_index(name)))
            lname = string.join(ref,'.')
            lindex = len(mcode.co_varnames)
            mcode.co_varnames.append(lname)
            prologue.append(STORE_FAST(lindex))
            load_op.arg = lindex

        mcs[0:0] = prologue

        mretops = []

        for op in mcs:
            if op.__class__ is RETURN_VALUE:
                newop = JUMP_FORWARD()
                mcs[mcs.index(op)] = newop
                mretops.append(newop)

        n = len(mcs)

        # insert the condition code
        mcs[n:n] = ccs.opcodes

        # store the returned value in Result
        store_result = STORE_FAST(Result_index)
        mcs.insert(n, store_result)

        # target the returns in the method to this store
        for op in mretops:
            op.label.op = store_result

        # the post condition will leave a value on the stack; lose it.
        # could just strip off the LOAD_CONST & RETURN_VALLUE at the
        # end of the function and scan for RETURN_VALUES in the
        # postcondition as a postcondition shouldn't be returning
        # things (certainly not other than None).
        new = POP_TOP()
        mcs.append(new)

        # redirect returns in the condition to the POP_TOP just
        # inserted...
        for op in cretops:
            op.label.op = new

        # actually return Result...
        mcs.append(LOAD_FAST(Result_index))
        mcs.append(RETURN_VALUE())

        # and add the new constants and names (to avoid core dumps!)
        mcode.co_consts  .extend(ccode.co_consts  )
        mcode.co_varnames.extend(ccode.co_varnames)
        mcode.co_names   .extend(ccode.co_names   )

        return meth.make_function()

    def find_old_refs(func):
        chaining = 0
        refs     = []
        ref      = []
        code     = func.func_code
        cs       = code.co_code

        i = 0
        
        while i < len(cs):
            op=cs[i]
            if not chaining:
                if op.__class__ is LOAD_GLOBAL:
                    if code.co_names[op.arg]=='Old':
                        chaining=1
            else:
                if op.__class__ is LOAD_ATTR:
                    ref.append(code.co_names[op.arg])
                else:
                    newop = LOAD_FAST(0)
                    cs[i-len(ref)-1:i] = [newop]
                    i = i - len(ref)
                    refs.append((ref,newop))
                    ref = []
                    chaining = 0
            i=i+1

        return refs
else: # if not __debug__
    def add_contracts(target_class,contracts_class):
        return target_class
    
# example

class Uncontracted:
    def __init__(self,x,y):
        self.x=x
        self.y=y
    def do(self):
#        self.x = self.x + 1 # sneaky!
        return self.x/self.y

class Contracts:
    def pre___init__(self,x,y):
        assert y <> 0
    def post_do(self):
        assert Old.self.x == self.x
        assert Old.self.y == self.y
        assert Result > 0, "Result was %s"%`Result`
    def class_invariants(self):
        assert self.x > 0

Contracted = add_contracts(Uncontracted,Contracts)





=== Added File Products/PythonMethod/zbytecodehacks/find_function_call.py ===
from code_editor import Function

from ops import *

def find_function_call(infunc,calledfuncname, allowkeywords=0, startindex=0):
    i = startindex
    code = infunc.func_code
    cs = code.co_code

    def match(op,name = calledfuncname):
        return getattr(op,'name',None) == name

    while i < len(cs):
        op = code.co_code[i]
        if match(op):
            try:
                if allowkeywords:
                    return simulate_stack_with_keywords(code,i)
                else:
                    return simulate_stack(code,i)
            except:
                i = i + 1
        i = i + 1
    if allowkeywords:
        return None,0
    else:
        return None
    
def call_stack_length_usage(arg):
    num_keyword_args=arg>>8
    num_regular_args=arg&0xFF
    return 2*num_keyword_args + num_regular_args

def simulate_stack(code,index_start):
    stack = []
    cs = code.co_code
    i, n = index_start, len(cs)
    
    while i < n:
        op = cs[i]
        if op.__class__ is CALL_FUNCTION and op.arg+1==len(stack):
            stack.append(op)
            return stack
        elif op.is_jump():
            i = cs.index(op.label.op)+1
        else:
            op.execute(stack)
            i = i + 1
    raise "no call found!"

def simulate_stack_with_keywords(code,index_start):
    stack = []
    cs = code.co_code
    i, n = index_start, len(cs)
    
    while i < n:
        op = cs[i]
        if op.__class__ is CALL_FUNCTION \
           and call_stack_length_usage(op.arg)+1==len(stack):
            stack.append(op)
            return stack, op.arg>>8
        elif op.is_jump():
            i = cs.index(op.label.op)+1
        else:
            op.execute(stack)
            i = i + 1
    raise "no call found!"


=== Added File Products/PythonMethod/zbytecodehacks/iif.py ===
from code_editor import Function
from ops import *
from find_function_call import find_function_call

def iifize(func):
    func = Function(func)
    cs = func.func_code.co_code

    while 1:
        stack = find_function_call(func,"iif")

        if stack is None:
            break

        load, test, consequent, alternative, call = stack

        cs.remove(load)

        jump1 = JUMP_IF_FALSE(alternative)
        cs.insert(cs.index(test)+1,jump1)

        jump2 = JUMP_FORWARD(call)
        cs.insert(cs.index(consequent)+1,jump2)
        
        cs.remove(call)

    cs = None
    
    return func.make_function()


=== Added File Products/PythonMethod/zbytecodehacks/inline.py ===
import dis
from code_editor import Function
from find_function_call import find_function_call
from ops import \
     LOAD_GLOBAL, RETURN_VALUE, SET_LINENO, CALL_FUNCTION, \
     JUMP_FORWARD, STORE_FAST

INLINE_MAX_DEPTH = 100

def inline(func, **funcs):
    func = Function(func)
    code = func.func_code

    for name, function in funcs.items():
        count = inline1(func, name, function)

        if count <> 0:
            fcode=function.func_code
            code.co_consts=code.co_consts+list(fcode.co_consts)
            code.co_varnames=code.co_varnames+list(fcode.co_varnames)
            code.co_names=code.co_names+list(fcode.co_names)
            code.co_stacksize=code.co_stacksize+fcode.co_stacksize

    return func.make_function()

def munge_code(function,code):
    f = Function(function)
    fcs = f.func_code.co_code
    i, n = 0, len(fcs)
    retops = []
    while i < n:
        op = fcs[i]
        if op.__class__ is RETURN_VALUE:
            # RETURN_VALUEs have to be replaced by JUMP_FORWARDs
            newop = JUMP_FORWARD()
            fcs[i] = newop
            retops.append(newop)
        elif op.op in dis.hasname:
            op.arg = op.arg + len(code.co_names)
        elif op.op in dis.haslocal:
            op.arg = op.arg + len(code.co_varnames)
        elif op.op in dis.hasconst:
            op.arg = op.arg + len(code.co_consts)
        # should we hack out SET_LINENOs? doesn't seem worth it.
        i = i + 1
    return fcs.opcodes, retops

def inline1(func,funcname,function):
    code = func.func_code
    cs = code.co_code
    count = 0
    defaults_added = 0

    while count < INLINE_MAX_DEPTH:
        stack, numkeywords = find_function_call(func,funcname,allowkeywords=1)

        if stack is None:
            return count

        count = count + 1

        load_func, posargs, kwargs, function_call = \
                   stack[0], stack[1:-2*numkeywords-1], stack[-2*numkeywords-1:-1], stack[-1]

        kw={}
        
        for i in range(0,len(kwargs),2):
            name = code.co_consts[kwargs[i].arg]
            valuesrc = kwargs[i+1]
            kw[name] = valuesrc

        varnames = list(function.func_code.co_varnames)

        for i in kw.keys():
            if i in varnames:
                if varnames.index(i) < len(posargs):
                    raise TypeError, "keyword parameter redefined"
            else:
                raise TypeError, "unexpected keyword argument: %s"%i

        # no varargs yet!
#          flags = function.func_code.co_flags

#          varargs = flags & (1<<2)
#          varkeys = flags & (1<<3)        


        args_got = len(kw) + len(posargs)
        args_expected = function.func_code.co_argcount

        if args_got > args_expected:
            raise TypeError,"too many arguments; expected %d, got %d"%(ac,len(lf) + len(posargs))
        elif args_got < args_expected:
            # default args?
            raise TypeError,"not enough arguments; expected %d, got %d"%(ac,len(lf) + len(posargs))
        
        cs.remove(load_func)

        local_index = len(code.co_varnames) 

        for insn in posargs:
            new = STORE_FAST(local_index)
            cs.insert(cs.index(insn)+1,new)
            labels = cs.find_labels(cs.index(new)+1)
            for label in labels:
                label.op = new
            local_index = local_index + 1

        for name, insn in kw.items():
            new = STORE_FAST(varnames.index(name) + len(code.co_varnames))
            cs.insert(cs.index(insn)+1,new)
            labels = cs.find_labels(cs.index(new)+1)
            for label in labels:
                label.op = new

        newops, retops = munge_code(function,code)

        call_index = cs.index(function_call)
        nextop = cs[call_index + 1]

        cs[call_index:call_index + 1] = newops

        for op in retops:
            op.label.op = nextop

    raise RuntimeError, "are we trying to inline a recursive function here?"

    



=== Added File Products/PythonMethod/zbytecodehacks/label.py ===
import struct

class Label:
    def __init__(self,byte=None):
        self.byte=byte
        self.__op=None
        self.absrefs=[]
        self.relrefs=[]
    def resolve(self,code):
        self.__op=code.opcodes[code.byte2op[self.byte]]
    def add_absref(self,byte):
        # request that the absolute address of self.op be written to
        # the argument of the opcode starting at byte in the
        # codestring
        self.absrefs.append(byte)
    def add_relref(self,byte):
        # request that the relative address of self.op be written to
        # the argument of the opcode starting at byte in the
        # codestring
        self.relrefs.append(byte)
    def __setattr__(self,attr,value):
        if attr == 'op':
            self.__op = value
        else:
            self.__dict__[attr] = value
    def __getattr__(self,attr):
        if attr == 'op':
            return self.__op
        else:
            raise AttributeError, attr
    def write_refs(self,cs):
        address=self.__op.byte
        for byte in self.absrefs:
            cs.seek(byte+1)
            cs.write(struct.pack('<h',address))
        for byte in self.relrefs:
            offset=address-byte-3
            cs.seek(byte+1)
            cs.write(struct.pack('<h',offset))



=== Added File Products/PythonMethod/zbytecodehacks/macro.py ===
import dis
from code_editor import Function
from find_function_call import find_function_call
from ops import *

MAX_MACRO_DEPTH = 100

_macros = {}

def add_macro(arg1,arg2=None):
    if arg2 is None:
        _macros[arg1.func_name] = arg1
    else:
        _macros[arg1]=arg2

def expand(func, macros = None):
    func = Function(func)
    code = func.func_code

    if macros is None:
        macros = _macros

    insertions = {}
    trips = 0

    while trips < MAX_MACRO_DEPTH:
        outercount = 0

        for name,macro in macros.items():
            count = expand1(func, name, macro)

            outercount = outercount + count

            if count <> 0 and not insertions.has_key(macro):
                fcode=macro.func_code
                code.co_consts=code.co_consts+list(fcode.co_consts)
                code.co_varnames=code.co_varnames+list(fcode.co_varnames)
                code.co_names=code.co_names+list(fcode.co_names)
                code.co_stacksize=code.co_stacksize+fcode.co_stacksize
                insertions[macro] = 0
                
        if not outercount:
            return func.make_function()
        trips = trips + 1
        
    raise RuntimeError, "unbounded recursion?!"

def expand_these(func,**macros):
    return expand(func,macros)

def remove_epilogue(cs):
    try:
        last,butone,buttwo = cs[-3:]
    except:
        return
    if last.__class__ is buttwo.__class__ is RETURN_VALUE:
        if butone.__class__ is LOAD_CONST:
            if cs.code.co_consts[butone.arg] is None:
                if not (cs.find_labels(-1) or cs.find_labels(-2)):
                    del cs[-2:]

def munge_code(function,code,imported_locals):
    f = Function(function)
    fcs = f.func_code.co_code

    if fcs[0].__class__ is SET_LINENO:
        del fcs[1:1 + 2*len(imported_locals)]
    else:
        del fcs[0:2*len(imported_locals)]

    # a nicety: let's see if the last couple of opcodes are necessary
    # (Python _always_ adds a LOAD_CONST None, RETURN_VALUE to the end
    # of a function, and I'd like to get rid of that if we can).

    remove_epilogue(fcs)    
    
    i, n = 0, len(fcs)
    retops = []
    while i < n:
        op = fcs[i]
        if op.__class__ is RETURN_VALUE:
            # RETURN_VALUEs have to be replaced by JUMP_FORWARDs
            newop = JUMP_FORWARD()
            fcs[i] = newop
            retops.append(newop)
        elif op.op in dis.hasname:
            op.arg = op.arg + len(code.co_names)
        elif op.op in dis.haslocal:
            localname = f.func_code.co_varnames[op.arg]
            op.arg = imported_locals.get(localname,op.arg + len(code.co_varnames))
        elif op.op in dis.hasconst:
            op.arg = op.arg + len(code.co_consts)
        # should we hack out SET_LINENOs? doesn't seem worth it.
        i = i + 1
    
    return fcs.opcodes, retops

def expand1(func,name,macro):
    code = func.func_code
    cs = code.co_code
    count = 0
    macrocode = macro.func_code

    while count < MAX_MACRO_DEPTH:
        stack = find_function_call(func,name)

        if stack is None:
            return count

        count = count + 1

        load_func, args, function_call = \
                   stack[0], stack[1:-1], stack[-1]

        args_got = len(args)
        args_expected = macrocode.co_argcount

        if args_got > args_expected:
            raise TypeError,"too many arguments; expected %d, got %d"%(args_expected,args_got)
        elif args_got < args_expected:
            # default args?
            raise TypeError,"not enough arguments; expected %d, got %d"%(args_expected,args_got)
        
        cs.remove(load_func)

        arg_names = macrocode.co_varnames[:macrocode.co_argcount]

        import_args = []
        normal_args = []
        
        for i in range(len(arg_names)):
            if arg_names[i][0] == '.':
                import_args.append(args[i])
            else:
                normal_args.append(args[i])

        imported_locals = {}
        
        for insn in import_args:
            cs.remove(insn)
            if insn.__class__ is LOAD_GLOBAL:
                name = code.co_names[insn.arg]
                var = global_to_local(code, name)                
            elif insn.__class__ is not LOAD_FAST:
                raise TypeError, "imported arg must be local"
            else:
                var = insn.arg
            argindex = macrocode.co_argcount + import_args.index(insn)
            argname = macrocode.co_varnames[argindex]
            imported_locals[argname] = var

        local_index = len(code.co_varnames) 

        for insn in normal_args:
            new = STORE_FAST(local_index + args.index(insn))
            cs.insert(cs.index(insn)+1,new)
            labels = cs.find_labels(cs.index(new)+1)
            for label in labels:
                label.op = new

        newops, retops = munge_code(macro,code,imported_locals)

        call_index = cs.index(function_call)
        nextop = cs[call_index + 1]

        cs[call_index:call_index + 1] = newops

        for op in retops:
            if cs.index(nextop) - cs.index(op) == 1:
                cs.remove(op)
            else:
                op.label.op = nextop

    raise RuntimeError, "are we trying to expand a recursive macro here?"

def global_to_local(code, name):
    """\
internal function to make a global variable into
a local one, for the case that setq is the first
reference to a variable.
Modifies a code object in-place. 
Return value is index into variable table
    """
    cs   = code.co_code

    index = len(code.co_varnames)
    code.co_varnames.append(name)

    for i in range(len(cs)):
        op = cs[i]
        if op.__class__ not in [LOAD_GLOBAL,STORE_GLOBAL]:
            continue
        thisname = code.co_names[op.arg]
        if thisname <> name:
            continue
        if op.__class__ is LOAD_GLOBAL:
            cs[i] = LOAD_FAST(index)
        else:
            cs[i] = STORE_FAST(index)
    return index













=== Added File Products/PythonMethod/zbytecodehacks/macros.py ===
from macro import add_macro
def main():
    def setq((x),v):
        x = v
        return v
    add_macro(setq)

    def pre_incr((x)):
        x = x + 1
        return x
    add_macro(pre_incr)

    def post_incr((x)):
        t = x
        x = x + 1
        return t
    add_macro(post_incr)

    def pre_decr((x)):
        x = x - 1
        return x
    add_macro(pre_decr)

    def post_decr((x)):
        t = x
        x = x + 1
        return t
    add_macro(post_decr)

    def add_set((x),v):
        x = x + v
        return x
    add_macro(add_set)

    def sub_set((x),v):
        x = x - v
        return x
    add_macro(sub_set)

    def mul_set((x),v):
        x = x * v
        return x
    add_macro(mul_set)

    def div_set((x),v):
        x = x / v
        return x
    add_macro(div_set)

    def mod_set((x),v):
        x = x % v
        return x
    add_macro(mod_set)

main()

def test():
    from macro import expand

    def f(x):
        i = 0
        while pre_incr(i) < len(x):
            if setq(c, x[i]) == 3:
                print c, 42
    x = expand(f)
    return x
    x(range(10))
        


=== Added File Products/PythonMethod/zbytecodehacks/opbases.py ===
import struct,dis,new
from label import Label

class ByteCode:
    pass

class GenericOneByteCode(ByteCode):
    def __init__(self,cs,code):
        pass
    def __repr__(self):
        return self.__class__.__name__
    def assemble(self,code):
        return self.opc
    def is_jump(self):
        return 0 
    def has_name(self):
        return 0
    def has_name_or_local(self):
        return self.has_name() or self.has_local()
    def has_local(self):
        return 0

class GenericThreeByteCode(GenericOneByteCode):
    def __init__(self,cs,code):
        GenericOneByteCode.__init__(self,cs,code)
        arg=cs.read(2)
        self.arg=struct.unpack('<h',arg)[0]
    def __repr__(self):
        return "%s %d"%(self.__class__.__name__, self.arg)
    def assemble(self,code):
        return self.opc+struct.pack('<h',self.arg)
    def user_init(self,arg):
        self.arg = arg

class Jump(GenericThreeByteCode):
    def __repr__(self):
        return "%s %s"%(self.__class__.__name__, `self.label`)
    def is_jump(self):
        return 1
    def user_init(self,arg):
        self.label.op = arg
    
class JRel(Jump):
    def __init__(self,cs,code):
        GenericThreeByteCode.__init__(self,cs,code)
        self.label = Label(cs.tell()+self.arg)
        code.add_label(self.label)
    def assemble(self,code):
        self.label.add_relref(self.byte)
        return self.opc+'\000\000'

class JAbs(Jump):
    def __init__(self,cs,code):
        GenericThreeByteCode.__init__(self,cs,code)
        self.label = Label(self.arg)
        code.add_label(self.label)
    def assemble(self,code):
        self.label.add_absref(self.byte)
        return self.opc+'\000\000'

class _NamedOpcode(GenericThreeByteCode):
    def user_init(self,arg):
        if type(arg) == type(1):
            self.arg = arg
        else:
            self.name = arg
    def __repr__(self):
        if hasattr(self,"name"):
            return "%s : %s"%(self.__class__.__name__,self.name)
        else:
            return "%s : %d"%(self.__class__.__name__,self.arg)            

class NameOpcode(_NamedOpcode):
    def __init__(self,cs,code):
        GenericThreeByteCode.__init__(self,cs,code)
        self.name = code.code.co_names[self.arg]
    def has_name(self):
        return 1
    def assemble(self,code):
        if hasattr(self,"name") and not hasattr(self,"arg"):
            self.arg = code.code.name_index(self.name)
        return GenericThreeByteCode.assemble(self,code)

class LocalOpcode(_NamedOpcode):
    def __init__(self,cs,code):
        GenericThreeByteCode.__init__(self,cs,code)
        self.name = code.code.co_varnames[self.arg]
    def has_local(self):
        return 1
    def assemble(self,code):
        if hasattr(self,"name") and not hasattr(self,"arg"):
            self.arg = code.code.local_index(self.name)
        return GenericThreeByteCode.assemble(self,code)
    


=== Added File Products/PythonMethod/zbytecodehacks/ops.py === (986/1086 lines abridged)
# this file is autogenerated by running
# from bytecodehacks.code_gen import write_ops
# write_ops.Main()

import opbases
from label import Label

_opbases = opbases
_Label = Label

del Label
del opbases

_bytecodes={}

class STOP_CODE(_opbases.GenericOneByteCode):
    op = 0
    opc = '\000'

    def __init__(self,cs=None,code=None):
        if cs is not None:
            _opbases.GenericOneByteCode.__init__(self,cs,code)
    def execute(self,stack):
        pass

_bytecodes[STOP_CODE.opc]=STOP_CODE

class POP_TOP(_opbases.GenericOneByteCode):
    op = 1
    opc = '\001'

    def __init__(self,cs=None,code=None):
        if cs is not None:
            _opbases.GenericOneByteCode.__init__(self,cs,code)
    def execute(self,stack):
        stack.pop()

_bytecodes[POP_TOP.opc]=POP_TOP

class ROT_TWO(_opbases.GenericOneByteCode):
    op = 2
    opc = '\002'

    def __init__(self,cs=None,code=None):
        if cs is not None:
            _opbases.GenericOneByteCode.__init__(self,cs,code)
    def execute(self,stack):
        stack[-2:]=[stack[-1],stack[-2]]

_bytecodes[ROT_TWO.opc]=ROT_TWO

[-=- -=- -=- 986 lines omitted -=- -=- -=-]

            self.user_init(csorarg)
    def execute(self,stack):
        raise "Exception!"

_bytecodes[RAISE_VARARGS.opc]=RAISE_VARARGS

class CALL_FUNCTION(_opbases.GenericThreeByteCode):
    op = 131
    opc = '\203'

    def __init__(self,csorarg,code=None):
        if code is not None:
            _opbases.GenericThreeByteCode.__init__(self,csorarg,code)
        else:
            self.user_init(csorarg)
    def execute(self,stack):
        num_keyword_args=self.arg>>8
        num_regular_args=self.arg&0xFF
        stack[-2*num_keyword_args-num_regular_args-1:]=[self]

_bytecodes[CALL_FUNCTION.opc]=CALL_FUNCTION

class MAKE_FUNCTION(_opbases.GenericThreeByteCode):
    op = 132
    opc = '\204'

    def __init__(self,csorarg,code=None):
        if code is not None:
            _opbases.GenericThreeByteCode.__init__(self,csorarg,code)
        else:
            self.user_init(csorarg)
    def execute(self,stack):
        stack[-self.arg-1:]=[self]

_bytecodes[MAKE_FUNCTION.opc]=MAKE_FUNCTION

class BUILD_SLICE(_opbases.GenericThreeByteCode):
    op = 133
    opc = '\205'

    def __init__(self,csorarg,code=None):
        if code is not None:
            _opbases.GenericThreeByteCode.__init__(self,csorarg,code)
        else:
            self.user_init(csorarg)
    def execute(self,stack):
        stack[-self.arg:]=[self]

_bytecodes[BUILD_SLICE.opc]=BUILD_SLICE



=== Added File Products/PythonMethod/zbytecodehacks/rationalize.py ===
import code_editor
from ops import *
import operator

CONDJUMP = [ JUMP_IF_TRUE, JUMP_IF_FALSE ]
UNCONDJUMP = [ JUMP_FORWARD, JUMP_ABSOLUTE ]
UNCOND = UNCONDJUMP + [ BREAK_LOOP, STOP_CODE, RETURN_VALUE, \
    RAISE_VARARGS ]
PYBLOCK = [ SETUP_LOOP, SETUP_EXCEPT, SETUP_FINALLY ]
PYENDBLOCK = [ POP_BLOCK ]

binaryops = {
    'BINARY_ADD': operator.add,
    'BINARY_SUBTRACT': operator.sub,
    'BINARY_MULTIPLY': operator.mul,
    'BINARY_DIVIDE': operator.div,
    'BINARY_MODULO': operator.mod,
    'BINARY_POWER': pow,
    'BINARY_LSHIFT': operator.lshift,
    'BINARY_RSHIFT': operator.rshift,
    'BINARY_AND': operator.and_,
    'BINARY_OR': operator.or_,
    'BINARY_XOR': operator.xor
  }

unaryops = {
    'UNARY_POS': operator.pos,
    'UNARY_NEG': operator.neg,
    'UNARY_NOT': operator.not_
  }

def rationalize(code):
    calculateConstants(code)
    strip_setlineno(code)
    simplifyjumps(code)
    removeconstjump(code)
    simplifyjumps(code)
    eliminateUnusedNames(code)
    eliminateUnusedLocals(code)

def calculateConstants(co):
    """Precalculate results of operations involving constants."""
    
    cs = co.co_code
    cc = co.co_consts
    
    stack = []
    i = 0
    while i < len(cs):
        op = cs[i]
        if binaryops.has_key(op.__class__.__name__):
            if stack[-1].__class__ is stack[-2].__class__ is LOAD_CONST:
                arg1 = cc[stack[-2].arg]
                arg2 = cc[stack[-1].arg]
                result = binaryops[op.__class__.__name__](arg1,arg2)

                if result in cc:
                    arg = cc.index(result)
                else:
                    arg = len(cc)
                    cc.append(result)
                    
                cs.remove(stack[-2])
                cs.remove(stack[-1])
                i = i - 2
                cs[i] = LOAD_CONST(arg)

                stack.pop()
                stack.pop()
                stack.append(cs[i])
            else:
                op.execute(stack)
        elif unaryops.has_key(op.__class__.__name__):
            if stack[-1].__class__ is LOAD_CONST:
                arg1 = cc[stack[-1].arg]
                result = unaryops[op.__class__.__name__](arg1)

                if result in cc:
                    arg = cc.index(result)
                else:
                    arg = len(cc)
                    cc.append(result)
                cs.remove(stack[-1])
                i = i - 1
                cs[i] = LOAD_CONST(arg)

                stack.pop()
                stack.append(cs[i])
            else:
                op.execute(stack)
        else:
            # this is almost certainly wrong
            try:
                op.execute(stack)
            except: pass
        i = i + 1

def strip_setlineno(co):
    """Take in an EditableCode object and strip the SET_LINENO bytecodes"""
    i = 0
    while i < len(co.co_code):
        op = co.co_code[i]
        if op.__class__ is SET_LINENO:
            co.co_code.remove(op)
        else:
            i = i + 1

def simplifyjumps(co):
    cs = co.co_code
    
    i = 0
    pyblockstack = [None]
    loopstack = [None]
    trystack = [None]
    firstlook = 1

    while i < len(cs):
        op = cs[i]

        # new pyblock?
        if firstlook:
            if op.__class__ in PYBLOCK:
                pyblockstack.append(op)
                if op.__class__ is SETUP_LOOP:
                    loopstack.append(op.label.op)
                else:
                    trystack.append(op.label.op)
            # end of pyblock?
            elif op.__class__ == POP_BLOCK:
                op2 = pyblockstack.pop()
                if op2.__class__ == SETUP_LOOP:
                    loopstack.pop()
                else:
                    trystack.pop()

        # Is the code inaccessible
        if i >= 1:
            if cs[i-1].__class__ in UNCOND and not (cs.find_labels(i) or \
                    op.__class__ in PYENDBLOCK):
                cs.remove(op)
                firstlook = 1
                continue

            # are we jumping from the statement before?
            if cs[i-1].__class__ in UNCONDJUMP:
                if cs[i-1].label.op == op:
                    cs.remove(cs[i-1])
                    firstlook = 1
                    continue

            # break before end of loop?
            elif cs[i-1].__class__ == BREAK_LOOP:
                if op.__class__ == POP_BLOCK:
                    cs.remove(cs[i-1])
                    firstlook = 1
                    continue

        # Do we have an unconditional jump to an unconditional jump?
        if op.__class__ in UNCONDJUMP:
            if op.label.op.__class__ in UNCONDJUMP:
                refop = op.label.op
                if op.__class__ == JUMP_FORWARD:
                    newop = JUMP_ABSOLUTE()
                    newop.label.op = refop.label.op
                    cs[i] = newop
                else: 
                    op.label.op = refop.label.op
                firstlook = 0
                continue

        # Do we have a conditional jump to a break?
        if op.__class__ in CONDJUMP and loopstack[-1]:
            destindex = cs.index(op.label.op)
            preendindex = cs.index(loopstack[-1])-2
            if cs[i+2].__class__ == BREAK_LOOP and cs[preendindex].__class__ \
                    == POP_TOP:
                if op.__class__ == JUMP_IF_FALSE:
                    newop = JUMP_IF_TRUE()
                else:
                    newop = JUMP_IF_FALSE()
                newop.label.op = cs[preendindex]
                cs[i] = newop
                cs.remove(cs[i+1])
                cs.remove(cs[i+1])
                cs.remove(cs[i+1])
                firstlook = 0
                continue
            elif cs[destindex+1].__class__ == BREAK_LOOP and \
                    cs[preendindex].__class__ == POP_TOP:
                op.label.op = cs[preendindex]
                cs.remove(cs[destindex])
                cs.remove(cs[destindex])
                cs.remove(cs[destindex])
                firstlook = 0
                continue

        firstlook = 1
        i = i+1


def removeconstjump(co):
    cs = co.co_code
    cc = co.co_consts
    
    i = 0
    while i < len(cs):
        op = cs[i]
        if op.__class__ in CONDJUMP and cs[i-1].__class__ == LOAD_CONST:
            if (op.__class__ == JUMP_IF_FALSE and cc[cs[i-1].arg]) or \
               (op.__class__ == JUMP_IF_TRUE and not cc[cs[i-1].arg]):
                cs.remove(cs[i-1])
                cs.remove(cs[i-1])
                cs.remove(cs[i-1])
                i = i-2
            else:
                cs.remove(cs[i-1])
                cs.remove(cs[i])
                newop = JUMP_FORWARD()
                newop.label.op = cs[cs.index(op.label.op)+1]
                cs[i-1] = newop
                i = i-1
        i = i+1

def eliminateUnusedNames(code):
    used_names = {}

    for op in code.co_code:
        if op.has_name():
            if hasattr(op,"arg"):
                arg = op.arg
            else:
                arg = op.arg = code.name_index(op.name)
            used_names[arg] = 1

    used_names = used_names.keys()
    used_names.sort()
    name_mapping = {}

    for i in range(len(used_names)):
        name_mapping[used_names[i]]=i

    newnames = []
    for i in range(len(code.co_names)):
        if i in used_names:
            newnames.append(code.co_names[i])
    code.co_names = newnames

    for op in code.co_code:
        if op.has_name():
            op.arg = name_mapping[op.arg]

def eliminateUnusedLocals(code):
    used_names = {}

    for op in code.co_code:
        if op.has_local():
            if hasattr(op,"arg"):
                arg = op.arg
            else:
                arg = op.arg = code.local_index(op.name)
            used_names[arg] = 1

    used_names = used_names.keys()
    used_names.sort()
    name_mapping = {}

    for i in range(len(used_names)):
        name_mapping[used_names[i]]=i

    newnames = []
    for i in range(len(code.co_varnames)):
        if i in used_names:
            newnames.append(code.co_varnames[i])
    code.co_varnames = newnames

    for op in code.co_code:
        if op.has_local():
            op.arg = name_mapping[op.arg]
    
    
                


=== Added File Products/PythonMethod/zbytecodehacks/tailr.py ===
from code_editor import Function
from find_function_call import find_function_call
from ops import *

def make_tail_recursive(func):
    func = Function(func)
    code = func.func_code
    cs = code.co_code
    
    index = 0

    while 1:
        stack = find_function_call(func,func.func_name,startindex=index)

        if stack is None:
            break

        index = cs.index(stack[-1])

        if cs[index + 1].__class__ is RETURN_VALUE:
            cs.remove(stack[0])
            newop = JUMP_ABSOLUTE()
            cs[index - 1:index] = [newop]
            newop.label.op = cs[0]
            del stack[0],stack[-1]

            nlocals = len(code.co_varnames)

            code.co_varnames = code.co_varnames + code.co_varnames

            for op in stack:
                cs.insert(cs.index(op)+1,STORE_FAST(stack.index(op)+nlocals))

            iindex = cs.index(newop)

            for i in range(len(stack)):
                cs.insert(iindex,STORE_FAST(i))
                cs.insert(iindex,LOAD_FAST(i+nlocals))

            index = iindex

    return func.make_function()

def _facr(n,c,p):
    if c <= n:
        return _facr(n,c+1,c*p)
    return p

def facr(n,_facr=_facr):
    return _facr(n,1,1l)

_factr = make_tail_recursive(_facr)

def factr(n,_factr=_factr):
    return _factr(n,1,1l)

def faci(n):
    p = 1l; c = 1;
    while c <= n:
        p = c*p
        c = c+1
    return p

import time

def suite(n,c=10,T=time.time):
    r = [0,0,0]
    for i in range(c):
        t=T(); facr(n); r[0] = T()-t + r[0]
        t=T(); factr(n); r[1] = T()-t + r[1]
        t=T(); faci(n); r[2] = T()-t + r[2]
    print "     recursive: 1.000000000000 (arbitrarily)"
    print "tail recursive:",r[1]/r[0]
    print "     iterative:",r[2]/r[0]
    


=== Added File Products/PythonMethod/zbytecodehacks/version ===
0.52z


=== Added File Products/PythonMethod/zbytecodehacks/xapply.py ===
"""\
xapply

Inspired by Don Beaudry's functor module.

xapply exports one public function, the eponymous xapply. xapply can
be thought of as `lazy apply' or `partial argument resolution'.

It takes a function and part of it's argument list, and returns a
function with the first parameters filled in. An example:

def f(x,y):
    return x+y

add1 = xapply(f,1)

add1(2) => 3

This xapply is not yet as general as that from the functor module, but
the functions return are as fast as normal function, i.e. twice as
fast as functors.

This may be generalised at some point in the future.
"""

import new,string,re,types
from ops import LOAD_FAST, LOAD_CONST
from code_editor import Function, InstanceMethod

def xapply_munge(code, args, except0=0):
    nconsts = len(code.co_consts)
    nvars = len(args)

    code.co_consts.extend(list(args))
    
    if except0:
        var2constlim = nvars+1
        var2constoff = nconsts-1
    else:
        var2constlim = nvars
        var2constoff = nconsts

    cs = code.co_code
    for i in range(len(cs)):
        op = cs[i]
        if op.__class__ is LOAD_FAST:
            if op.arg == 0 and except0:
                continue
            if op.arg < var2constlim:
                cs[i] = LOAD_CONST(op.arg + var2constoff)
            else:
                op.arg = op.arg - nvars
    code.co_varnames = code.co_varnames[nvars:]
    code.co_argcount = code.co_argcount - nvars

def xapply_func(func,args):
    f = Function(func)
    xapply_munge(f.func_code,args,0)
    return f.make_function()

def xapply_meth(meth,args):
    im = InstanceMethod(meth)
    xapply_munge(im.im_func.func_code,args,1)
    return im.make_instance_method()

def xapply(callable,*args):
    """ xapply(callable,arg1,arg2,...) -> callable

if

f=xapply(callable,arg1,arg2,...,argn)

then

f(arg<n+1>,....argm)

is equivalent to

callable(arg1,...,argn,arg<n+1>,..argm)

callable currently must be a function or instance method, and keyword
arguments are currently not allowed.

"""
    callable_type=type(callable)
    if callable_type is types.FunctionType:
        return xapply_func(callable,args)
    elif callable_type is types.UnboundMethodType:
        return xapply_meth(callable,args)
    else:
        raise "nope"







More information about the zopeorg-checkins mailing list