[Subversion] / BytecodeAssembler / peak / util / assembler.txt  

Diff of /BytecodeAssembler/peak/util/assembler.txt

Parent Directory | Revision Log

version 2188, Thu Jun 15 06:19:17 2006 UTC version 2190, Fri Jun 16 05:56:11 2006 UTC
Line 66 
Line 66 
     42      42
   
   
 Opcodes, Jumps, and Labels  Opcodes and Arguments
 ==========================  =====================
   
 ``Code`` objects have methods for all of CPython's symbolic opcodes.  Generally  ``Code`` objects have methods for all of CPython's symbolic opcodes.  Generally
 speaking, each method accepts either zero or one argument, depending on whether  speaking, each method accepts either zero or one argument, depending on whether
 the opcode accepts an argument.  the opcode accepts an argument.
   
 But while Python bytecode always encodes arguments as 16 or 32-bit integers,  Python bytecode always encodes opcode arguments as 16 or 32-bit integers, but
 you will generally pass actual names or values to ``Code`` methods, and the  sometimes these numbers are actually offsets into a sequence of names or
 ``Code`` object will take care of maintaining the necessary lookup tables and  constants.  ``Code`` objects take care of maintaining these sequences for you,
 translation to integer bytecode arguments.  allowing you to just pass in a name or value directly, instead of needing to
   keep track of what numbers map to what names or values.
   
   The name or value you pass in to such methods will be looked up in the
   appropriate table (see `Code Attributes`_ below for a list), and if not found,
   it will be added::
   
       >>> c = Code()
       >>> c.co_consts, c.co_varnames, c.co_names
       ([None], [], [])
   
       >>> c.LOAD_CONST(42)
       >>> c.LOAD_FAST('x')
       >>> c.LOAD_GLOBAL('y')
       >>> c.LOAD_NAME('z')
   
       >>> c.co_consts, c.co_varnames, c.co_names
       ([None, 42], ['x'], ['y', 'z'])
   
   The one exception to this automatic addition feature is that opcodes referring
   to "free" or "cell" variables will not automatically add new names, because the
   names need to be defined first::
   
 Labels and backpatching forward references::      >>> c.LOAD_DEREF('q')
       Traceback (most recent call last):
         ...
       NameError: ('Undefined free or cell var', 'q')
   
   In general, opcode methods take the same arguments as their Python bytecode
   equivalent.  But there are a few special cases.
   
   
   Call Arguments
   --------------
   
   First, the ``CALL_FUNCTION()``, ``CALL_FUNCTION_VAR()``, ``CALL_FUNCTION_KW()``,
   and ``CALL_FUNCTION_VAR_KW()`` methods all take *two* arguments, both of which
   are optional.  (The ``_VAR`` and ``_KW`` suffixes in the method names indicate
   whether or not a ``*args`` or ``**kwargs`` or both are also present on the
   stack, in addition to the explicit positional and keyword arguments.)
   
   The first argument of each of these methods, is the number of positional
   arguments on the stack, and the second is the number of keyword/value pairs on
   the stack (to be used as keyword arguments).  Both default to zero if not
   supplied::
   
     >>> c = Code()      >>> c = Code()
     >>> ref = c.JUMP_ABSOLUTE()     # jump w/unspecified target      >>> c.LOAD_CONST(type)
     >>> c.LOAD_CONST(1)      >>> c.LOAD_CONST(27)
     >>> ref()                       # resolve the forward reference      >>> c.CALL_FUNCTION(1)      # 1 positional, no keywords
     >>> c.RETURN_VALUE()      >>> c.RETURN_VALUE()
     >>> dis(c.code())  
       0           0 JUMP_ABSOLUTE            6  
                   3 LOAD_CONST               1 (1)  
             >>    6 RETURN_VALUE  
   
       >>> eval(c.code())          # computes type(27)
       <type 'int'>
   
     >>> c = Code()      >>> c = Code()
     >>> lbl = c.label()     # create a label at this point in the code      >>> c.LOAD_CONST(dict)
     >>> c.LOAD_CONST(1)      >>> c.LOAD_CONST('x')
     >>> ref = c.JUMP_ABSOLUTE(lbl)  # and jump to it      >>> c.LOAD_CONST(42)
       >>> c.CALL_FUNCTION(0,1)    # no positional, 1 keyword
       >>> c.RETURN_VALUE()
   
       >>> eval(c.code())          # computes dict(x=42)
       {'x': 42}
   
   
   Jumps, Labels, and Blocks
   -------------------------
   
   Opcodes that perform jumps or refer to addresses (which includes block-setup
   opcodes like ``SETUP_LOOP`` and ``SETUP_FINALLY``) can be invoked in one of
   two ways.  First, if you are jumping backwards (with ``JUMP_ABSOLUTE`` or
   ``CONTINUE_LOOP``), you can obtain the target bytecode offset using the
   ``.label()`` method, and then later pass that label into the appropriate
   method::
   
       >>> c = Code()
       >>> my_label = c.label()        # create a label at the start of the code
   
       >>> c.LOAD_CONST(42)
       >>> c.JUMP_ABSOLUTE(my_label)   # now jump back to it
       <...>
   
     >>> dis(c.code())      >>> dis(c.code())
       0     >>    0 LOAD_CONST               1 (1)        0     >>    0 LOAD_CONST               1 (42)
                   3 JUMP_ABSOLUTE            0                    3 JUMP_ABSOLUTE            0
   
   But if you are jumping *forward* (or setting up a loop or a try block), you
   will need to call the jump or setup method without any arguments, and save the
   return value.  The return value of all jump and block-setup methods is a
   "forward reference" object that can be called later to indicate that the
   desired jump target has been reached::
   
       >>> c = Code()
       >>> forward = c.JUMP_ABSOLUTE() # create a jump and a forward reference
   
       >>> c.LOAD_CONST(42)            # this is what we want to skip over
   
       >>> forward()   # calling the reference changes the jump to point here
       >>> c.LOAD_CONST(23)
       >>> c.RETURN_VALUE()
   
       >>> dis(c.code())
         0           0 JUMP_ABSOLUTE            6
                     3 LOAD_CONST               1 (42)
               >>    6 LOAD_CONST               2 (23)
                     9 RETURN_VALUE
   
       >>> eval(c.code())
       23
   
   Note that ``Code`` objects do not currently implement any special handling
   for "block" operations like ``SETUP_EXCEPT`` and ``END_FINALLY``.  You will
   probably need to manually adjust the predicted stack size when working with
   these opcodes.  See the section below on `Code Attributes`_ for information
   on the ``stack_size`` and ``co_stacksize`` attributes of ``Code`` objects.
   
   
   Other Special Opcodes
   ---------------------
   
   The ``MAKE_CLOSURE`` method takes an argument for the number of default values
   on the stack, just like the "real" Python opcode.  However, it also has an
   an additional required argument: the number of closure cells on the stack.
   The Python interpreter normally gets this number from a code object that's on
   the stack, but ``Code`` objects need this value in order to update the
   current stack size, for purposes of computing the required total stack size::
   
       >>> def x(a,b):     # a simple closure example
       ...     def y():
       ...         return a+b
       ...     return y
   
 Code Generation API      >>> c = Code()
 ===================      >>> c.co_cellvars = ('a','b')
   
       >>> c.LOAD_CLOSURE('a')
       >>> c.LOAD_CLOSURE('b')
       >>> c.LOAD_CONST(None)  # in real code, this'd be a Python code constant
       >>> c.MAKE_CLOSURE(0,2) # no defaults, 2 free vars in the new function
   
   
   High-Level Code Generation
   ==========================
   
   Typical real-life code generation use cases call for transforming tree-like
   data structures into bytecode, rather than linearly outputting instructions.
   ``Code`` objects provide for this using a simple but high-level transformation
   API.
   
 Code generation from tuples, lists, dicts, and local variable names::  ``Code`` objects may be *called*, passing in one or more arguments.  Each
   argument will have bytecode generated for it, according to its type:
   
     >>> from peak.util.assembler import Const, Call, Global, Local  
   Simple Constants
   ----------------
   
   If an argument is an integer, long, float, complex, string, unicode, boolean,
   ``None``, or Python code object, it is treated as though it was passed to
   the ``LOAD_CONST`` method directly::
   
     >>> c = Code()      >>> c = Code()
     >>> c( [Local('x'), (Local('y'),Local('z'))] )  # push a value on the stack      >>> c(1, 2L, 3.0, 4j+5, "6", u"7", False, None, c.code())
     >>> dis(c.code())      >>> dis(c.code())
       0           0 LOAD_FAST                0 (x)        0           0 LOAD_CONST               1 (1)
                   3 LOAD_FAST                1 (y)                    3 LOAD_CONST               2 (2L)
                   6 LOAD_FAST                2 (z)                    6 LOAD_CONST               3 (3.0)
                   9 BUILD_TUPLE              2                    9 LOAD_CONST               4 ((5+4j))
                  12 BUILD_LIST               2                   12 LOAD_CONST               5 ('6')
                    15 LOAD_CONST               6 (u'7')
                    18 LOAD_CONST               7 (False)
                    21 LOAD_CONST               0 (None)
                    24 LOAD_CONST               8 (<code object <lambda> at ...>)
   
 And with constants, dictionaries, globals, and calls::  
   
   Simple Containers
   -----------------
   
   If an argument is a tuple, list, or dictionary, code is generated to
   reconstruct the given data, recursively::
   
     >>> c = Code()      >>> c = Code()
     >>> c.return_( [Global('type'), Const(27)] )     # push and RETURN_VALUE      >>> c({1:(2,"3"), 4:[5,6]})
     >>> dis(c.code())      >>> dis(c.code())
       0           0 LOAD_GLOBAL              0 (type)        0           0 BUILD_MAP                0
                   3 LOAD_CONST               1 (27)                    3 DUP_TOP
                   6 BUILD_LIST               2                    4 LOAD_CONST               1 (1)
                   9 RETURN_VALUE                    7 LOAD_CONST               2 (2)
                    10 LOAD_CONST               3 ('3')
                    13 BUILD_TUPLE              2
                    16 ROT_THREE
                    17 STORE_SUBSCR
                    18 DUP_TOP
                    19 LOAD_CONST               4 (4)
                    22 LOAD_CONST               5 (5)
                    25 LOAD_CONST               6 (6)
                    28 BUILD_LIST               2
                    31 ROT_THREE
                    32 STORE_SUBSCR
   
   
   Arbitrary Constants
   -------------------
   
   The ``Const`` wrapper allows you to treat any object as a literal constant,
   regardless of its type::
   
       >>> from peak.util.assembler import Const
   
     >>> c = Code()      >>> c = Code()
     >>> c( {Const('x'): Const(123)} )      >>> c( Const( (1,2,3) ) )
       >>> dis(c.code())
         0           0 LOAD_CONST               1 ((1, 2, 3))
   
   As you can see, the above creates code that references an actual tuple as
   a constant, rather than generating code to recreate the tuple using a series of
   ``LOAD_CONST`` operations followed by a ``BUILD_TUPLE``.
   
   
   Local and Global Names
   ----------------------
   
   The ``Local`` and ``Global`` wrappers take a name, and load either a local or
   global variable, respectively::
   
       >>> from peak.util.assembler import Global, Local
   
       >>> c( Local('x'), Global('y') )
       >>> dis(c.code())
         0           0 LOAD_CONST               1 ((1, 2, 3))
                     3 LOAD_FAST                0 (x)
                     6 LOAD_GLOBAL              0 (y)
   
   As with simple constants and ``Const`` wrappers, these objects can be used to
   construct more complex expressions, like ``{a:(b,c)}``::
   
       >>> c = Code()
       >>> c( {Local('a'): (Local('b'), Local('c'))} )
     >>> dis(c.code())      >>> dis(c.code())
       0           0 BUILD_MAP                0        0           0 BUILD_MAP                0
                   3 DUP_TOP                    3 DUP_TOP
                   4 LOAD_CONST               1 ('x')                    4 LOAD_FAST                0 (a)
                   7 LOAD_CONST               2 (123)                    7 LOAD_FAST                1 (b)
                  10 ROT_THREE                   10 LOAD_FAST                2 (c)
                  11 STORE_SUBSCR                   13 BUILD_TUPLE              2
                    16 ROT_THREE
                    17 STORE_SUBSCR
   
   If the code object is not using "fast locals" (i.e. ``CO_OPTIMIZED`` isn't
   set), local variables will be dereferenced using ``LOAD_NAME`` instead of
   ``LOAD_FAST``, and if the referenced local name is a "cell" or "free"
   variable, ``LOAD_DEREF`` is used instead::
   
       >>> from peak.util.assembler import CO_OPTIMIZED
     >>> c = Code()      >>> c = Code()
     >>> c(Call(Global('type'), (Const(1),)))      >>> c.co_flags &= ~CO_OPTIMIZED
       >>> c.co_cellvars = ('y',)
       >>> c.co_freevars = ('z',)
       >>> c( Local('x'), Local('y'), Local('z') )
     >>> dis(c.code())      >>> dis(c.code())
         0           0 LOAD_NAME                0 (x)
                     3 LOAD_DEREF               0 (y)
                     6 LOAD_DEREF               1 (z)
   
   
   Calling Functions and Methods
   -----------------------------
   
       >>> from peak.util.assembler import Call
   
   The ``Call`` wrapper takes 1-4 arguments: the expression to be called, a
   sequence of positional arguments, a sequence of keyword/value pairs for
   explicit keyword arguments, an "*" argument, and a "**" argument.  To omit any
   of the optional arguments, just pass in an empty sequence in its place::
   
       >>> c = Code()
       >>> c( Call(Global('type'), [Const(27)]) )
   
       >>> dis(c.code())   # type(27)
       0           0 LOAD_GLOBAL              0 (type)        0           0 LOAD_GLOBAL              0 (type)
                   3 LOAD_CONST               1 (1)                    3 LOAD_CONST               1 (27)
                   6 CALL_FUNCTION            1                    6 CALL_FUNCTION            1
   
     >>> c = Code()      >>> c = Code()
     >>> c(Call(Global('getattr'), (Const(1), Const('__class__'))))      >>> c(Call(Global('dict'), (), [('x', 42)]))
   
       >>> dis(c.code())   # dict(x=42)
         0           0 LOAD_GLOBAL              0 (dict)
                     3 LOAD_CONST               1 ('x')
                     6 LOAD_CONST               2 (42)
                     9 CALL_FUNCTION            256
   
       >>> c = Code()
       >>> c(Call(Global('foo'), (), (), Local('args'), Local('kw')))
   
       >>> dis(c.code())   # foo(*args, **kw)
         0           0 LOAD_GLOBAL              0 (foo)
                     3 LOAD_FAST                0 (args)
                     6 LOAD_FAST                1 (kw)
                     9 CALL_FUNCTION_VAR_KW     0
   
   
   Returning Values
   ----------------
   
   The ``Return(target)`` wrapper generates code for its target, followed by
   a ``RETURN_VALUE`` opcode::
   
       >>> from peak.util.assembler import Return
   
       >>> c = Code()
       >>> c( Return(1) )
     >>> dis(c.code())      >>> dis(c.code())
       0           0 LOAD_GLOBAL              0 (getattr)        0           0 LOAD_CONST               1 (1)
                   3 LOAD_CONST               1 (1)                    3 RETURN_VALUE
                   6 LOAD_CONST               2 ('__class__')  
                   9 CALL_FUNCTION            2  
   
 ``Call`` objects take 1-4 arguments: the expression to be called, a sequence  ``Code`` objects also have a ``return_()`` method that provides a more compact
 of positional arguments, a sequence of keyword/value pairs for explicit keyword  spelling of the same thing::
 arguments, an "*" argument, and a "**" argument.  To omit any of the optional  
 arguments, just pass in an empty sequence in its place::  
   
     >>> c = Code()      >>> c = Code()
     >>> c.return_(      >>> c.return_((1,2))
     ...     Call(Global('foo'), [Local('q')], [('x',Const(1))],  
     ...          Local('starargs'), Local('kwargs'))  
     ... )  
     >>> dis(c.code())      >>> dis(c.code())
       0           0 LOAD_GLOBAL              0 (foo)        0           0 LOAD_CONST               1 (1)
                   3 LOAD_FAST                0 (q)                    3 LOAD_CONST               2 (2)
                   6 LOAD_CONST               1 ('x')                    6 BUILD_TUPLE              2
                   9 LOAD_CONST               2 (1)                    9 RETURN_VALUE
                  12 LOAD_FAST                1 (starargs)  
                  15 LOAD_FAST                2 (kwargs)  Both ``Return`` and ``return_()`` can be used with no argument, in which case
                  18 CALL_FUNCTION_VAR_KW   257  ``None`` is returned::
                  21 RETURN_VALUE  
       >>> c = Code()
 Code generation is extensible: any 1-argument callables passed in will      >>> c.return_()
 be passed the code object during generation.  (The return value, if any, is      >>> c( Return() )
 ignored.)  You can even use ``Code`` methods, if they don't have any required      >>> dis(c.code())
 arguments::        0           0 LOAD_CONST               0 (None)
                     3 RETURN_VALUE
                     4 LOAD_CONST               0 (None)
                     7 RETURN_VALUE
   
   
   Using Forward References As Targets
   -----------------------------------
   
   The forward reference callbacks returned by jump operations are also usable
   as code generation targets, indicating that the jump should go to the
   current location.  For example::
   
       >>> c = Code()
       >>> forward = c.JUMP_FORWARD()
       >>> c( 1, 2, forward, Return(3) )
       >>> dis(c.code())
         0           0 JUMP_FORWARD             6 (to 9)
                     3 LOAD_CONST               1 (1)
                     6 LOAD_CONST               2 (2)
                >>   9 LOAD_CONST               3 (3)
                    12 RETURN_VALUE
   
   
   Custom Code Generation
   ======================
   
   Code generation is extensible: you can use any callable as a code-generation
   target.  It will be called with exactly one argument: the code object.  It can
   then perform whatever operations are desired.
   
   In the most trivial case, you can use any unbound ``Code`` method as a code
   generation target, e.g.::
   
     >>> c = Code()      >>> c = Code()
     >>> c.LOAD_GLOBAL('foo')      >>> c.LOAD_GLOBAL('foo')
Line 187 
Line 453 
                   3 DUP_TOP                    3 DUP_TOP
                   4 CALL_FUNCTION            0                    4 CALL_FUNCTION            0
   
 This basically means you can create a simple AST of callable objects to drive  As you can see, the ``Code.DUP_TOP()`` is called on the code instance, causing
 code generation, with a lot of the grunt work automatically handled for you.  a ``DUP_TOP`` opcode to be output.  This is sometimes a handy trick for
   accessing values that are already on the stack.  More commonly, however, you'll
   want to implement more sophisticated callables, perhaps something like::
   
       >>> from peak.util.assembler import ast_curry
   
       >>> def TryFinally(block1, block2, code=None):
       ...     if code is None:
       ...         return ast_curry(TryFinally, block1, block2)
       ...     fwd = code.SETUP_FINALLY()
       ...     code(block1, Code.POP_BLOCK, None, fwd, block2, Code.END_FINALLY)
   
       >>> def Stmt(value, code=None):
       ...     if code is None:
       ...         return ast_curry(Stmt, value)
       ...     code( value, Code.POP_TOP )
   
       >>> c = Code()
       >>> c( TryFinally(Stmt(1), Stmt(2)) )
       >>> dis(c.code())
         0           0 SETUP_FINALLY            8 (to 11)
                     3 LOAD_CONST               1 (1)
                     6 POP_TOP
                     7 POP_BLOCK
                     8 LOAD_CONST               0 (None)
               >>   11 LOAD_CONST               2 (2)
                    14 POP_TOP
                    15 END_FINALLY
   
   The ``ast_curry()`` utility function returns an ``instancemethod`` chain that
   binds the given arguments to the given function, creating a hashable and
   comparable data structure -- a trivial sort of "AST node".  Just follow the
   code pattern above, using a ``code=None`` final argument, and returning a
   curried version of the function if ``code is None``.  Otherwise, your function
   should simply do whatever is needed to "generate" the arguments.
   
   This is exactly the same way that ``Const``, ``Call``, ``Local``, etc. are
   implemented within ``peak.util.assembler``.
   
   The ``ast_curry()`` utility function isn't quite perfect; due to a quirk of the
   ``instancemethod`` type, it can't save arguments whose value is ``None``: if
   you pass a ``None`` argument to ``ast_curry()``, it will be replaced with a
   special ``nil`` object that tests as false, and generates a ``None`` constant
   when code is generated for it.  If your function accepts any arguments that
   might have a value of ``None``, you must correctly handle the cases where you
   receive a value of ``nil`` (found in ``peak.util.assembler``) instead of
   ``None``.
   
   However, if you can use ``ast_curry()`` to generate your AST nodes, you will
   have objects that are hashable and comparable by default, as long as none of
   your child nodes are unhashable or incomparable.  This can be useful for
   algorithms that require comparing AST subtrees, such as common subexpression
   elimination.
   
   
 Setting the Code's Calling Signature  Setting the Code's Calling Signature
Line 197 
Line 515 
 The simplest way to set up the calling signature for a ``Code`` instance is  The simplest way to set up the calling signature for a ``Code`` instance is
 to clone an existing function or code object's signature, using the  to clone an existing function or code object's signature, using the
 ``Code.from_function()`` or ``Code.from_code()`` classmethods.  These methods  ``Code.from_function()`` or ``Code.from_code()`` classmethods.  These methods
 create a new code object whose calling signature (number and names of  create a new ``Code`` instance whose calling signature (number and names of
 arguments) matches that of the original function or code objects::  arguments) matches that of the original function or code objects::
   
     >>> def f1(a,b,*c,**d):      >>> def f1(a,b,*c,**d):
     ...     pass      ...     pass
   
     >>> c1 = Code.from_function(f1)      >>> c = Code.from_function(f1)
     >>> c1.co_argcount      >>> f2 = new.function(c.code(), globals())
     2  
     >>> c1.co_varnames  
     ['a', 'b', 'c', 'd']  
   
     >>> import inspect      >>> import inspect
   
     >>> inspect.getargspec(f1)      >>> inspect.getargspec(f1)
     (['a', 'b'], 'c', 'd', None)      (['a', 'b'], 'c', 'd', None)
   
     >>> f2 = new.function(c1.code(), globals())  
     >>> inspect.getargspec(f2)      >>> inspect.getargspec(f2)
     (['a', 'b'], 'c', 'd', None)      (['a', 'b'], 'c', 'd', None)
   
Line 334 
Line 649 
 stack_size  stack_size
     The predicted height of the runtime value stack, as of the current opcode.      The predicted height of the runtime value stack, as of the current opcode.
     Its value is automatically updated by most opcodes, but you may want to      Its value is automatically updated by most opcodes, but you may want to
     save and restore it for things like try/finally blocks.      save and restore it for things like try/finally blocks.  If you increase
       the value of this attribute, you should also update the ``co_stacksize``
       attribute if it is less than the new ``stack_size``.
   
 co_freevars  co_freevars
     A tuple of strings naming a function's "cell" variables.  Defaults to an      A tuple of strings naming a function's "cell" variables.  Defaults to an
Line 378 
Line 695 
   
 co_stacksize  co_stacksize
     The maximum amount of stack space the code will require to run.  This      The maximum amount of stack space the code will require to run.  This
     value is usually updated automatically as you generate code.      value is usually updated automatically as you generate code.  However, if
       you manually set a new ``stack_size`` that is larger than the current
       ``co_stacksize``, you should increase the ``co_stacksize`` to match, so
       that ``co_stacksize`` is always the largest stack size the code will
       generate at runtime.
   
   
   
Line 607 
Line 928 
                  24 BUILD_SLICE              3                   24 BUILD_SLICE              3
                  27 BUILD_SLICE              3                   27 BUILD_SLICE              3
   
     XXX Need tests for MAKE_CLOSURE/MAKE_FUNCTION  Stack levels for MAKE_FUNCTION/MAKE_CLOSURE::
   
       >>> c = Code()
       >>> c.MAKE_FUNCTION(0)
       Traceback (most recent call last):
         ...
       AssertionError: Stack underflow
   
 Labels and backpatching forward references::      >>> c.LOAD_CONST(1)
       >>> c.LOAD_CONST(2) # simulate being a function
       >>> c.MAKE_FUNCTION(1)
       >>> c.stack_size
       1
   
     >>> c = Code()      >>> c = Code()
     >>> ref = c.JUMP_ABSOLUTE()      >>> c.MAKE_CLOSURE(0, 0)
       Traceback (most recent call last):
         ...
       AssertionError: Stack underflow
   
     >>> c.LOAD_CONST(1)      >>> c.LOAD_CONST(1)
     >>> ref()      >>> c.LOAD_CONST(2) # simulate being a function
     >>> c.RETURN_VALUE()      >>> c.MAKE_CLOSURE(1, 0)
     >>> dis(c.code())      >>> c.stack_size
       0           0 JUMP_ABSOLUTE            6      1
                   3 LOAD_CONST               1 (1)  
             >>    6 RETURN_VALUE  
   
     >>> c = Code()      >>> c = Code()
     >>> ref = c.JUMP_FORWARD()  
     >>> c.LOAD_CONST(1)      >>> c.LOAD_CONST(1)
     >>> ref()      >>> c.LOAD_CONST(2)
     >>> c.RETURN_VALUE()      >>> c.LOAD_CONST(3) # simulate being a function
     >>> dis(c.code())      >>> c.MAKE_CLOSURE(1, 1)
       0           0 JUMP_FORWARD             3 (to 6)      >>> c.stack_size
                   3 LOAD_CONST               1 (1)      1
             >>    6 RETURN_VALUE  
   
   Labels and backpatching forward references::
   
     >>> c = Code()      >>> c = Code()
     >>> lbl = c.label()      >>> lbl = c.label()
Line 640 
Line 973 
       ...        ...
     AssertionError: Relative jumps can't go backwards      AssertionError: Relative jumps can't go backwards
   
     >>> c = Code()  
     >>> lbl = c.label()  
     >>> c.LOAD_CONST(1)  
     >>> ref = c.JUMP_ABSOLUTE(lbl)  
     >>> dis(c.code())  
       0     >>    0 LOAD_CONST               1 (1)  
                   3 JUMP_ABSOLUTE            0  
   
   
 "Call" combinations::  "Call" combinations::
   
Line 706 
Line 1031 
 TODO  TODO
 ====  ====
   
 * Test free/cell ops (LOAD_CLOSURE, LOAD_DEREF, STORE_DEREF)  * Constant folding
 * Test MAKE_FUNCTION/MAKE_CLOSURE      * ast_type(node): called function, Const, or node.__class__
         * tuples are Const if their contents are; no other types are Const
       * ast_children(node): tuple of argument values for curried types, const value,
         or empty tuple.  If node is a tuple, the value must be flattened.
       * is_const(node): ast_type(node) is Const
       * const_value(node): ast_children(node)[0]
       * Call() does the actual folding
   
   * Inline builtins (getattr, operator.getitem, etc.) to opcodes
       * Getattr/Op/Unary("symbol", arg1 [, arg2]) node types -> Call() if folding
       * Call() translates functions back to Ops if inlining
   
   * Pretty printing and short-naming of ASTs
   
   * Test NAME vs. FAST operators flag checks/sets
   
 * Test code flags generation/cloning  * Test code flags generation/cloning
   
   * Document block handling (SETUP_*, POP_BLOCK, END_FINALLY) and make sure the
     latter two have correct stack tracking
   
   
   


Generate output suitable for use with a patch program
Legend:
Removed from v.2188  
changed lines
  Added in v.2190

cvs-admin@eby-sarna.com

Powered by ViewCVS 1.0-dev

ViewCVS and CVS Help