From arigo at codespeak.net Fri Dec 1 03:44:42 2006 From: arigo at codespeak.net (arigo at codespeak.net) Date: Fri, 1 Dec 2006 03:44:42 +0100 (CET) Subject: [pypy-svn] r35184 - in pypy/branch/jit-real-world/pypy: annotation config interpreter interpreter/test module/_pickle_support objspace/flow tool Message-ID: <20061201024442.5A66D1006E@code0.codespeak.net> Author: arigo Date: Fri Dec 1 03:43:34 2006 New Revision: 35184 Added: pypy/branch/jit-real-world/pypy/tool/pairtype.py - copied unchanged from r35175, pypy/branch/jit-real-world/pypy/annotation/pairtype.py Removed: pypy/branch/jit-real-world/pypy/interpreter/opcodeorder.py Modified: pypy/branch/jit-real-world/pypy/annotation/pairtype.py pypy/branch/jit-real-world/pypy/config/pypyoption.py pypy/branch/jit-real-world/pypy/interpreter/baseobjspace.py pypy/branch/jit-real-world/pypy/interpreter/eval.py pypy/branch/jit-real-world/pypy/interpreter/function.py pypy/branch/jit-real-world/pypy/interpreter/generator.py pypy/branch/jit-real-world/pypy/interpreter/nestedscope.py pypy/branch/jit-real-world/pypy/interpreter/pycode.py pypy/branch/jit-real-world/pypy/interpreter/pyframe.py pypy/branch/jit-real-world/pypy/interpreter/pyopcode.py pypy/branch/jit-real-world/pypy/interpreter/test/test_interpreter.py pypy/branch/jit-real-world/pypy/interpreter/test/test_pyframe.py pypy/branch/jit-real-world/pypy/interpreter/typedef.py pypy/branch/jit-real-world/pypy/module/_pickle_support/maker.py pypy/branch/jit-real-world/pypy/objspace/flow/flowcontext.py pypy/branch/jit-real-world/pypy/objspace/flow/framestate.py pypy/branch/jit-real-world/pypy/tool/stdlib_opcode.py Log: (pedronis, arigo) Intermediate check-in #2. More seriously, we rewrote the interpreter main loop in a style that should be easier for the JIT to grasp, but also -- possibly -- easier for human beings to grasp. A bit. Many levels of wrapping of stuff into other stuff are avoided now, and the PyFrame class hierarchy was completely removed. There is a possibly nice way to add opcodes from outside, with config options to enable them. The drawback is that the core main loop uses modern RPython magic. Translation not tested. It makes py.py slower by 30% but we hope that pypy-c will be faster. Modified: pypy/branch/jit-real-world/pypy/annotation/pairtype.py ============================================================================== --- pypy/branch/jit-real-world/pypy/annotation/pairtype.py (original) +++ pypy/branch/jit-real-world/pypy/annotation/pairtype.py Fri Dec 1 03:43:34 2006 @@ -1,63 +1,4 @@ -""" -Two magic tricks for classes: +# no longer there, sorry +# XXX fix all these imports - class X: - __metaclass__ = extendabletype - ... - - # in some other file... - class __extend__(X): - ... # and here you can add new methods and class attributes to X - -Mostly useful together with the second trick, which lets you build -methods whose 'self' is a pair of objects instead of just one: - - class __extend__(pairtype(X, Y)): - attribute = 42 - def method((x, y), other, arguments): - ... - - pair(x, y).attribute - pair(x, y).method(other, arguments) - -This finds methods and class attributes based on the actual -class of both objects that go into the pair(), with the usual -rules of method/attribute overriding in (pairs of) subclasses. - -For more information, see test_pairtype. -""" - -class extendabletype(type): - """A type with a syntax trick: 'class __extend__(t)' actually extends - the definition of 't' instead of creating a new subclass.""" - def __new__(cls, name, bases, dict): - if name == '__extend__': - for cls in bases: - for key, value in dict.items(): - if key == '__module__': - continue - # XXX do we need to provide something more for pickling? - setattr(cls, key, value) - return None - else: - return super(extendabletype, cls).__new__(cls, name, bases, dict) - - -def pair(a, b): - """Return a pair object.""" - tp = pairtype(a.__class__, b.__class__) - return tp((a, b)) # tp is a subclass of tuple - -pairtypecache = {} - -def pairtype(cls1, cls2): - """type(pair(a,b)) is pairtype(a.__class__, b.__class__).""" - try: - pair = pairtypecache[cls1, cls2] - except KeyError: - name = 'pairtype(%s, %s)' % (cls1.__name__, cls2.__name__) - bases1 = [pairtype(base1, cls2) for base1 in cls1.__bases__] - bases2 = [pairtype(cls1, base2) for base2 in cls2.__bases__] - bases = tuple(bases1 + bases2) or (tuple,) # 'tuple': ultimate base - pair = pairtypecache[cls1, cls2] = extendabletype(name, bases, {}) - return pair +from pypy.tool.pairtype import * Modified: pypy/branch/jit-real-world/pypy/config/pypyoption.py ============================================================================== --- pypy/branch/jit-real-world/pypy/config/pypyoption.py (original) +++ pypy/branch/jit-real-world/pypy/config/pypyoption.py Fri Dec 1 03:43:34 2006 @@ -37,6 +37,9 @@ ["cpython", "ast"], "ast", cmdline='--compiler'), + OptionDescription("opcodes", "opcodes to enable in the interpreter", [ + ]), + BoolOption("nofaking", "disallow faking in the object space", default=False, requires=[ Modified: pypy/branch/jit-real-world/pypy/interpreter/baseobjspace.py ============================================================================== --- pypy/branch/jit-real-world/pypy/interpreter/baseobjspace.py (original) +++ pypy/branch/jit-real-world/pypy/interpreter/baseobjspace.py Fri Dec 1 03:43:34 2006 @@ -174,6 +174,10 @@ from pypy.config.pypyoption import pypy_optiondescription config = Config(pypy_optiondescription) self.config = config + + # import extra modules for side-effects, possibly based on config + import pypy.interpreter.nestedscope # register *_DEREF bytecodes + self.interned_strings = {} self.pending_actions = [] self.setoptions(**kw) Modified: pypy/branch/jit-real-world/pypy/interpreter/eval.py ============================================================================== --- pypy/branch/jit-real-world/pypy/interpreter/eval.py (original) +++ pypy/branch/jit-real-world/pypy/interpreter/eval.py Fri Dec 1 03:43:34 2006 @@ -147,28 +147,3 @@ new_fastlocals_w[i] = w_value self.setfastscope(new_fastlocals_w) - - -class EvalFrame(Frame): - - def resume(self): - "Resume the execution of the frame from its current state." - executioncontext = self.space.getexecutioncontext() - executioncontext.enter(self) - try: - result = self.eval(executioncontext) - rstack.resume_point("evalframe", self, executioncontext, returns=result) - finally: - executioncontext.leave(self) - return result - - # running a frame is usually the same as resuming it from its - # initial state, but not for generator frames - run = resume - - def eval(self, executioncontext): - "Abstract method to override." - raise TypeError, "abstract" - - def hide(self): - return False Modified: pypy/branch/jit-real-world/pypy/interpreter/function.py ============================================================================== --- pypy/branch/jit-real-world/pypy/interpreter/function.py (original) +++ pypy/branch/jit-real-world/pypy/interpreter/function.py Fri Dec 1 03:43:34 2006 @@ -9,7 +9,6 @@ from pypy.interpreter.error import OperationError from pypy.interpreter.baseobjspace import Wrappable from pypy.interpreter.eval import Code -from pypy.interpreter.pyframe import PyFrame from pypy.interpreter.argument import Arguments, ArgumentsFromValuestack class Function(Wrappable): Modified: pypy/branch/jit-real-world/pypy/interpreter/generator.py ============================================================================== --- pypy/branch/jit-real-world/pypy/interpreter/generator.py (original) +++ pypy/branch/jit-real-world/pypy/interpreter/generator.py Fri Dec 1 03:43:34 2006 @@ -1,38 +1,5 @@ from pypy.interpreter.error import OperationError from pypy.interpreter.baseobjspace import Wrappable -from pypy.interpreter.eval import EvalFrame -from pypy.interpreter.pyframe import ControlFlowException, ExitFrame - -# -# Generator support. Note that GeneratorFrame is not a subclass of PyFrame. -# PyCode objects use a custom subclass of both PyFrame and GeneratorFrame -# when they need to interpret Python bytecode that is a generator. -# Otherwise, GeneratorFrame could also be used to define, say, -# built-in generators (which are usually done in CPython as functions -# that return iterators). -# - -class GeneratorFrameMixin(object): - "A frame attached to a generator." - _mixin_ = True - - def run(self): - "Build a generator-iterator." - return self.space.wrap(GeneratorIterator(self)) - - ### extra opcodes ### - - # XXX mmmh, GeneratorFrame is supposed to be independent from - # Python bytecode... Well, it is. These are not used when - # GeneratorFrame is used with other kinds of Code subclasses. - - def RETURN_VALUE(f): # overridden - raise SGeneratorReturn() - - def YIELD_VALUE(f): - w_yieldedvalue = f.valuestack.pop() - raise SYieldValue(w_yieldedvalue) - YIELD_STMT = YIELD_VALUE # misnamed in old versions of dis.opname class GeneratorIterator(Wrappable): @@ -42,7 +9,6 @@ self.space = frame.space self.frame = frame self.running = False - self.exhausted = False def descr__reduce__(self, space): from pypy.interpreter.mixedmodule import MixedModule @@ -54,7 +20,6 @@ tup = [ w(self.frame), w(self.running), - w(self.exhausted), ] return space.newtuple([new_inst, space.newtuple(tup)]) @@ -69,34 +34,21 @@ if self.running: raise OperationError(space.w_ValueError, space.wrap('generator already executing')) - if self.exhausted: + if self.frame.frame_finished_execution: raise OperationError(space.w_StopIteration, space.w_None) self.running = True try: try: - return self.frame.resume() + w_result = self.frame.execute_frame() except OperationError: - self.exhausted = True + # errors finish a frame + self.frame.frame_finished_execution = True raise + # if the frame is now marked as finished, it was RETURNed from + if self.frame.frame_finished_execution: + raise OperationError(space.w_StopIteration, space.w_None) + else: + return w_result # YIELDed finally: self.frame.f_back = None self.running = False - -# -# the specific ControlFlowExceptions used by generators -# - -class SYieldValue(ControlFlowException): - """Signals a 'yield' statement. - Argument is the wrapped object to return.""" - - def __init__(self, w_yieldvalue): - self.w_yieldvalue = w_yieldvalue - - def action(self, frame): - raise ExitFrame(self.w_yieldvalue) - -class SGeneratorReturn(ControlFlowException): - """Signals a 'return' statement inside a generator.""" - def emptystack(self, frame): - raise OperationError(frame.space.w_StopIteration, frame.space.w_None) Modified: pypy/branch/jit-real-world/pypy/interpreter/nestedscope.py ============================================================================== --- pypy/branch/jit-real-world/pypy/interpreter/nestedscope.py (original) +++ pypy/branch/jit-real-world/pypy/interpreter/nestedscope.py Fri Dec 1 03:43:34 2006 @@ -1,5 +1,4 @@ from pypy.interpreter.error import OperationError -from pypy.interpreter.pyopcode import PyInterpFrame from pypy.interpreter import function, pycode, pyframe from pypy.interpreter.baseobjspace import Wrappable from pypy.interpreter.mixedmodule import MixedModule @@ -59,7 +58,12 @@ content, uid(self)) -class PyNestedScopeFrame(PyInterpFrame): +super_initialize_frame_scopes = pyframe.PyFrame.initialize_frame_scopes +super_fast2locals = pyframe.PyFrame.fast2locals +super_locals2fast = pyframe.PyFrame.locals2fast + + +class __extend__(pyframe.PyFrame): """This class enhances a standard frame with nested scope abilities, i.e. handling of cell/free variables.""" @@ -69,28 +73,47 @@ # variables coming from a parent function in which i'm nested # 'closure' is a list of Cell instances: the received free vars. - def __init__(self, space, code, w_globals, closure): - PyInterpFrame.__init__(self, space, code, w_globals, closure) + cells = None + + def initialize_frame_scopes(self, closure): + super_initialize_frame_scopes(self, closure) + code = self.pycode ncellvars = len(code.co_cellvars) nfreevars = len(code.co_freevars) - if closure is None: - if nfreevars: - raise OperationError(space.w_TypeError, - space.wrap("directly executed code object " - "may not contain free variables")) - closure = [] - else: - if len(closure) != nfreevars: - raise ValueError("code object received a closure with " + if not nfreevars: + if not ncellvars: + return # no self.cells needed - fast path + if closure is None: + closure = [] + elif closure is None: + space = self.space + raise OperationError(space.w_TypeError, + space.wrap("directly executed code object " + "may not contain free variables")) + if len(closure) != nfreevars: + raise ValueError("code object received a closure with " "an unexpected number of free variables") self.cells = [Cell() for i in range(ncellvars)] + closure def getclosure(self): + if self.cells is None: + return None ncellvars = len(self.pycode.co_cellvars) # not part of the closure return self.cells[ncellvars:] + def _getcells(self): + return self.cells + + def _setcellvars(self, cellvars): + ncellvars = len(self.pycode.co_cellvars) + if len(cellvars) != ncellvars: + raise OperationError(self.space.w_TypeError, + self.space.wrap("bad cellvars")) + if self.cells is not None: + self.cells[:ncellvars] = cellvars + def fast2locals(self): - PyInterpFrame.fast2locals(self) + super_fast2locals(self) # cellvars are values exported to inner scopes # freevars are values coming from outer scopes freevarnames = self.pycode.co_cellvars + self.pycode.co_freevars @@ -106,7 +129,7 @@ self.space.setitem(self.w_locals, w_name, w_value) def locals2fast(self): - PyInterpFrame.locals2fast(self) + super_locals2fast(self) freevarnames = self.pycode.co_cellvars + self.pycode.co_freevars for i in range(len(freevarnames)): name = freevarnames[i] @@ -121,6 +144,8 @@ cell.set(w_value) def init_cells(self): + if self.cells is None: + return args_to_copy = self.pycode._args_as_cellvars for i in range(len(args_to_copy)): argnum = args_to_copy[i] @@ -136,13 +161,13 @@ ### extra opcodes ### - def LOAD_CLOSURE(f, varindex): + def LOAD_CLOSURE(f, varindex, *ignored): # nested scopes: access the cell object cell = f.cells[varindex] w_value = f.space.wrap(cell) f.valuestack.push(w_value) - def LOAD_DEREF(f, varindex): + def LOAD_DEREF(f, varindex, *ignored): # nested scopes: access a variable through its cell object cell = f.cells[varindex] try: @@ -160,7 +185,7 @@ else: f.valuestack.push(w_value) - def STORE_DEREF(f, varindex): + def STORE_DEREF(f, varindex, *ignored): # nested scopes: access a variable through its cell object w_newvalue = f.valuestack.pop() #try: @@ -170,7 +195,7 @@ # raise cell.set(w_newvalue) - def MAKE_CLOSURE(f, numdefaults): + def MAKE_CLOSURE(f, numdefaults, *ignored): w_codeobj = f.valuestack.pop() codeobj = f.space.interp_w(pycode.PyCode, w_codeobj) if codeobj.magic >= 0xa0df281: # CPython 2.5 AST branch merge Modified: pypy/branch/jit-real-world/pypy/interpreter/pycode.py ============================================================================== --- pypy/branch/jit-real-world/pypy/interpreter/pycode.py (original) +++ pypy/branch/jit-real-world/pypy/interpreter/pycode.py Fri Dec 1 03:43:34 2006 @@ -9,7 +9,7 @@ from pypy.interpreter import eval from pypy.interpreter.error import OperationError from pypy.interpreter.gateway import NoneNotWrapped -from pypy.interpreter.baseobjspace import ObjSpace, W_Root +from pypy.interpreter.baseobjspace import ObjSpace, W_Root from pypy.rlib.rarithmetic import intmask # helper @@ -49,29 +49,6 @@ cpython_magic, = struct.unpack("= pythonopcode.HAVE_ARGUMENT: - oparg = self.nextarg() - while True: - if opcode == pythonopcode.EXTENDED_ARG: - opcode = self.nextop() - oparg = oparg<<16 | self.nextarg() - if opcode < pythonopcode.HAVE_ARGUMENT: - raise pyframe.BytecodeCorruption - continue - else: - fn = self.dispatch_table_w_arg[opcode] - fn(self, oparg) - break + # For the sequel, force 'next_instr' to be unsigned for performance + next_instr = r_uint(self.last_instr) + opcode = ord(co_code[next_instr]) + next_instr += 1 + if space.config.objspace.logbytecodes: + space.bytecodecounts[opcode] = space.bytecodecounts.get(opcode, 0) + 1 + + if opcode >= HAVE_ARGUMENT: + lo = ord(co_code[next_instr]) + hi = ord(co_code[next_instr+1]) + next_instr += 2 + oparg = (hi << 8) | lo else: - fn = self.dispatch_table_no_arg[opcode] - fn(self) + oparg = 0 - def nextop(self): - c = self.pycode.co_code[self.next_instr] - self.next_instr += 1 - return ord(c) - - def nextarg(self): - lo = self.nextop() - hi = self.nextop() - return (hi<<8) + lo + while opcode == opcodedesc.EXTENDED_ARG.index: + opcode = ord(co_code[next_instr]) + if opcode < HAVE_ARGUMENT: + raise BytecodeCorruption + lo = ord(co_code[next_instr+1]) + hi = ord(co_code[next_instr+2]) + next_instr += 3 + oparg = (oparg << 16) | (hi << 8) | lo + + if opcode == opcodedesc.RETURN_VALUE.index: + w_returnvalue = self.valuestack.pop() + block = self.unrollstack(SReturnValue.kind) + if block is None: + self.frame_finished_execution = True # for generators + return w_returnvalue + else: + unroller = SReturnValue(w_returnvalue) + next_instr = block.handle(self, unroller) + continue # now inside a 'finally' block + + if opcode == opcodedesc.YIELD_VALUE.index: + w_yieldvalue = self.valuestack.pop() + return w_yieldvalue + + if opcode == opcodedesc.END_FINALLY.index: + # unlike CPython, when we reach this opcode the value stack has + # always been set up as follows (topmost first): + # [exception type or None] + # [exception value or None] + # [wrapped stack unroller ] + self.valuestack.pop() # ignore the exception type + self.valuestack.pop() # ignore the exception value + w_unroller = self.valuestack.pop() + unroller = self.space.interpclass_w(w_unroller) + if isinstance(unroller, SuspendedUnroller): + # go on unrolling the stack + block = self.unrollstack(unroller.kind) + if block is None: + self.frame_finished_execution = True # for generators + return unroller.nomoreblocks() + else: + next_instr = block.handle(self, unroller) + continue + + if we_are_translated(): + for opdesc in unrolling_opcode_descs: + # static checks to skip this whole case if necessary + if not opdesc.is_enabled(space): + continue + if not hasattr(pyframe.PyFrame, opdesc.methodname): + continue # e.g. for JUMP_FORWARD, implemented above + + if opcode == opdesc.index: + # dispatch to the opcode method + meth = getattr(self, opdesc.methodname) + res = meth(oparg, next_instr) + # !! warning, for the annotator the next line is not + # comparing an int and None - you can't do that. + # Instead, it's constant-folded to either True or False + if res is not None: + next_instr = res + break + else: + self.MISSING_OPCODE(oparg, next_instr) + + else: # when we are not translated, a list lookup is much faster + methodname = opcode_method_names[opcode] + res = getattr(self, methodname)(oparg, next_instr) + if res is not None: + next_instr = res + + def unrollstack(self, unroller_kind): + while not self.blockstack.empty(): + block = self.blockstack.pop() + if (block.handling_mask & unroller_kind) != 0: + return block + return None + + def unrollstack_and_jump(self, unroller): + block = self.unrollstack(unroller.kind) + if block is None: + raise BytecodeCorruption("misplaced bytecode - should not return") + return block.handle(self, unroller) ### accessor functions ### @@ -110,10 +225,10 @@ # the 'self' argument of opcode implementations is called 'f' # for historical reasons - def NOP(f): + def NOP(f, *ignored): pass - def LOAD_FAST(f, varindex): + def LOAD_FAST(f, varindex, *ignored): # access a local variable directly w_value = f.fastlocals_w[varindex] if w_value is None: @@ -122,11 +237,11 @@ raise OperationError(f.space.w_UnboundLocalError, f.space.wrap(message)) f.valuestack.push(w_value) - def LOAD_CONST(f, constindex): + def LOAD_CONST(f, constindex, *ignored): w_const = f.getconstant_w(constindex) f.valuestack.push(w_const) - def STORE_FAST(f, varindex): + def STORE_FAST(f, varindex, *ignored): w_newvalue = f.valuestack.pop() f.fastlocals_w[varindex] = w_newvalue #except: @@ -139,16 +254,16 @@ # print "co_nlocals", f.pycode.co_nlocals # raise - def POP_TOP(f): + def POP_TOP(f, *ignored): f.valuestack.pop() - def ROT_TWO(f): + def ROT_TWO(f, *ignored): w_1 = f.valuestack.pop() w_2 = f.valuestack.pop() f.valuestack.push(w_1) f.valuestack.push(w_2) - def ROT_THREE(f): + def ROT_THREE(f, *ignored): w_1 = f.valuestack.pop() w_2 = f.valuestack.pop() w_3 = f.valuestack.pop() @@ -156,7 +271,7 @@ f.valuestack.push(w_3) f.valuestack.push(w_2) - def ROT_FOUR(f): + def ROT_FOUR(f, *ignored): w_1 = f.valuestack.pop() w_2 = f.valuestack.pop() w_3 = f.valuestack.pop() @@ -166,11 +281,11 @@ f.valuestack.push(w_3) f.valuestack.push(w_2) - def DUP_TOP(f): + def DUP_TOP(f, *ignored): w_1 = f.valuestack.top() f.valuestack.push(w_1) - def DUP_TOPX(f, itemcount): + def DUP_TOPX(f, itemcount, *ignored): assert 1 <= itemcount <= 5, "limitation of the current interpreter" for i in range(itemcount): w_1 = f.valuestack.top(itemcount-1) @@ -182,7 +297,7 @@ UNARY_CONVERT = unaryoperation("repr") UNARY_INVERT = unaryoperation("invert") - def BINARY_POWER(f): + def BINARY_POWER(f, *ignored): w_2 = f.valuestack.pop() w_1 = f.valuestack.pop() w_result = f.space.pow(w_1, w_2, f.space.w_None) @@ -203,7 +318,7 @@ BINARY_XOR = binaryoperation("xor") BINARY_OR = binaryoperation("or_") - def INPLACE_POWER(f): + def INPLACE_POWER(f, *ignored): w_2 = f.valuestack.pop() w_1 = f.valuestack.pop() w_result = f.space.inplace_pow(w_1, w_2) @@ -228,18 +343,18 @@ w_result = f.space.getslice(w_obj, w_start, w_end) f.valuestack.push(w_result) - def SLICE_0(f): + def SLICE_0(f, *ignored): f.slice(f.space.w_None, f.space.w_None) - def SLICE_1(f): + def SLICE_1(f, *ignored): w_start = f.valuestack.pop() f.slice(w_start, f.space.w_None) - def SLICE_2(f): + def SLICE_2(f, *ignored): w_end = f.valuestack.pop() f.slice(f.space.w_None, w_end) - def SLICE_3(f): + def SLICE_3(f, *ignored): w_end = f.valuestack.pop() w_start = f.valuestack.pop() f.slice(w_start, w_end) @@ -249,18 +364,18 @@ w_newvalue = f.valuestack.pop() f.space.setslice(w_obj, w_start, w_end, w_newvalue) - def STORE_SLICE_0(f): + def STORE_SLICE_0(f, *ignored): f.storeslice(f.space.w_None, f.space.w_None) - def STORE_SLICE_1(f): + def STORE_SLICE_1(f, *ignored): w_start = f.valuestack.pop() f.storeslice(w_start, f.space.w_None) - def STORE_SLICE_2(f): + def STORE_SLICE_2(f, *ignored): w_end = f.valuestack.pop() f.storeslice(f.space.w_None, w_end) - def STORE_SLICE_3(f): + def STORE_SLICE_3(f, *ignored): w_end = f.valuestack.pop() w_start = f.valuestack.pop() f.storeslice(w_start, w_end) @@ -269,69 +384,69 @@ w_obj = f.valuestack.pop() f.space.delslice(w_obj, w_start, w_end) - def DELETE_SLICE_0(f): + def DELETE_SLICE_0(f, *ignored): f.deleteslice(f.space.w_None, f.space.w_None) - def DELETE_SLICE_1(f): + def DELETE_SLICE_1(f, *ignored): w_start = f.valuestack.pop() f.deleteslice(w_start, f.space.w_None) - def DELETE_SLICE_2(f): + def DELETE_SLICE_2(f, *ignored): w_end = f.valuestack.pop() f.deleteslice(f.space.w_None, w_end) - def DELETE_SLICE_3(f): + def DELETE_SLICE_3(f, *ignored): w_end = f.valuestack.pop() w_start = f.valuestack.pop() f.deleteslice(w_start, w_end) - def STORE_SUBSCR(f): + def STORE_SUBSCR(f, *ignored): "obj[subscr] = newvalue" w_subscr = f.valuestack.pop() w_obj = f.valuestack.pop() w_newvalue = f.valuestack.pop() f.space.setitem(w_obj, w_subscr, w_newvalue) - def DELETE_SUBSCR(f): + def DELETE_SUBSCR(f, *ignored): "del obj[subscr]" w_subscr = f.valuestack.pop() w_obj = f.valuestack.pop() f.space.delitem(w_obj, w_subscr) - def PRINT_EXPR(f): + def PRINT_EXPR(f, *ignored): w_expr = f.valuestack.pop() print_expr(f.space, w_expr) - def PRINT_ITEM_TO(f): + def PRINT_ITEM_TO(f, *ignored): w_stream = f.valuestack.pop() w_item = f.valuestack.pop() if f.space.is_w(w_stream, f.space.w_None): w_stream = sys_stdout(f.space) # grumble grumble special cases print_item_to(f.space, w_item, w_stream) - def PRINT_ITEM(f): + def PRINT_ITEM(f, *ignored): w_item = f.valuestack.pop() print_item(f.space, w_item) - def PRINT_NEWLINE_TO(f): + def PRINT_NEWLINE_TO(f, *ignored): w_stream = f.valuestack.pop() if f.space.is_w(w_stream, f.space.w_None): w_stream = sys_stdout(f.space) # grumble grumble special cases print_newline_to(f.space, w_stream) - def PRINT_NEWLINE(f): + def PRINT_NEWLINE(f, *ignored): print_newline(f.space) - def BREAK_LOOP(f): - raise pyframe.SBreakLoop - - def CONTINUE_LOOP(f, startofloop): - raise pyframe.SContinueLoop(startofloop) + def BREAK_LOOP(f, *ignored): + next_instr = f.unrollstack_and_jump(SBreakLoop.singleton) + return next_instr + + def CONTINUE_LOOP(f, startofloop, *ignored): + unroller = SContinueLoop(startofloop) + next_instr = f.unrollstack_and_jump(unroller) + return next_instr - def RAISE_VARARGS(f, nbargs): - # we use the .app.py file to prepare the exception/value/traceback - # but not to actually raise it, because we cannot use the 'raise' - # statement to implement RAISE_VARARGS + def RAISE_VARARGS(f, nbargs, *ignored): space = f.space if nbargs == 0: operror = space.getexecutioncontext().sys_exc_info() @@ -339,7 +454,9 @@ raise OperationError(space.w_TypeError, space.wrap("raise: no active exception to re-raise")) # re-raise, no new traceback obj will be attached - raise pyframe.SApplicationException(operror) + f.last_exception = operror + raise Reraise + w_value = w_traceback = space.w_None if nbargs >= 3: w_traceback = f.valuestack.pop() if nbargs >= 2: w_value = f.valuestack.pop() @@ -357,16 +474,13 @@ space.wrap("raise: arg 3 must be a traceback or None")) operror.application_traceback = tb # re-raise, no new traceback obj will be attached - raise pyframe.SApplicationException(operror) + f.last_exception = operror + raise Reraise - def LOAD_LOCALS(f): + def LOAD_LOCALS(f, *ignored): f.valuestack.push(f.w_locals) - def RETURN_VALUE(f): - w_returnvalue = f.valuestack.pop() - raise pyframe.SReturnValue(w_returnvalue) - - def EXEC_STMT(f): + def EXEC_STMT(f, *ignored): w_locals = f.valuestack.pop() w_globals = f.valuestack.pop() w_prog = f.valuestack.pop() @@ -386,25 +500,11 @@ if plain: f.setdictscope(w_locals) - def POP_BLOCK(f): + def POP_BLOCK(f, *ignored): block = f.blockstack.pop() block.cleanup(f) # the block knows how to clean up the value stack - def END_FINALLY(f): - # unlike CPython, when we reach this opcode the value stack has - # always been set up as follows (topmost first): - # [exception type or None] - # [exception value or None] - # [wrapped stack unroller ] - f.valuestack.pop() # ignore the exception type - f.valuestack.pop() # ignore the exception value - w_unroller = f.valuestack.pop() - unroller = f.space.interpclass_w(w_unroller) - if isinstance(unroller, pyframe.SuspendedUnroller): - # re-raise the unroller, if any - raise unroller.flowexc - - def BUILD_CLASS(f): + def BUILD_CLASS(f, *ignored): w_methodsdict = f.valuestack.pop() w_bases = f.valuestack.pop() w_name = f.valuestack.pop() @@ -415,12 +515,12 @@ w_bases, w_methodsdict) f.valuestack.push(w_newclass) - def STORE_NAME(f, varindex): + def STORE_NAME(f, varindex, *ignored): w_varname = f.getname_w(varindex) w_newvalue = f.valuestack.pop() f.space.set_str_keyed_item(f.w_locals, w_varname, w_newvalue) - def DELETE_NAME(f, varindex): + def DELETE_NAME(f, varindex, *ignored): w_varname = f.getname_w(varindex) try: f.space.delitem(f.w_locals, w_varname) @@ -431,7 +531,7 @@ message = "name '%s' is not defined" % f.space.str_w(w_varname) raise OperationError(f.space.w_NameError, f.space.wrap(message)) - def UNPACK_SEQUENCE(f, itemcount): + def UNPACK_SEQUENCE(f, itemcount, *ignored): w_iterable = f.valuestack.pop() try: items = f.space.unpackiterable(w_iterable, itemcount) @@ -441,29 +541,29 @@ for item in items: f.valuestack.push(item) - def STORE_ATTR(f, nameindex): + def STORE_ATTR(f, nameindex, *ignored): "obj.attributename = newvalue" w_attributename = f.getname_w(nameindex) w_obj = f.valuestack.pop() w_newvalue = f.valuestack.pop() f.space.setattr(w_obj, w_attributename, w_newvalue) - def DELETE_ATTR(f, nameindex): + def DELETE_ATTR(f, nameindex, *ignored): "del obj.attributename" w_attributename = f.getname_w(nameindex) w_obj = f.valuestack.pop() f.space.delattr(w_obj, w_attributename) - def STORE_GLOBAL(f, nameindex): + def STORE_GLOBAL(f, nameindex, *ignored): w_varname = f.getname_w(nameindex) w_newvalue = f.valuestack.pop() f.space.set_str_keyed_item(f.w_globals, w_varname, w_newvalue) - def DELETE_GLOBAL(f, nameindex): + def DELETE_GLOBAL(f, nameindex, *ignored): w_varname = f.getname_w(nameindex) f.space.delitem(f.w_globals, w_varname) - def LOAD_NAME(f, nameindex): + def LOAD_NAME(f, nameindex, *ignored): if f.w_locals is not f.w_globals: w_varname = f.getname_w(nameindex) w_value = f.space.finditem(f.w_locals, w_varname) @@ -472,7 +572,7 @@ return f.LOAD_GLOBAL(nameindex) # fall-back - def LOAD_GLOBAL(f, nameindex): + def LOAD_GLOBAL(f, nameindex, *ignored): w_varname = f.getname_w(nameindex) w_value = f.space.finditem(f.w_globals, w_varname) if w_value is None: @@ -485,7 +585,7 @@ f.space.wrap(message)) f.valuestack.push(w_value) - def DELETE_FAST(f, varindex): + def DELETE_FAST(f, varindex, *ignored): if f.fastlocals_w[varindex] is None: varname = f.getlocalvarname(varindex) message = "local variable '%s' referenced before assignment" % varname @@ -493,25 +593,25 @@ f.fastlocals_w[varindex] = None - def BUILD_TUPLE(f, itemcount): + def BUILD_TUPLE(f, itemcount, *ignored): items = [f.valuestack.pop() for i in range(itemcount)] items.reverse() w_tuple = f.space.newtuple(items) f.valuestack.push(w_tuple) - def BUILD_LIST(f, itemcount): + def BUILD_LIST(f, itemcount, *ignored): items = [f.valuestack.pop() for i in range(itemcount)] items.reverse() w_list = f.space.newlist(items) f.valuestack.push(w_list) - def BUILD_MAP(f, zero): + def BUILD_MAP(f, zero, *ignored): if zero != 0: - raise pyframe.BytecodeCorruption + raise BytecodeCorruption w_dict = f.space.newdict() f.valuestack.push(w_dict) - def LOAD_ATTR(f, nameindex): + def LOAD_ATTR(f, nameindex, *ignored): "obj.attributename" w_attributename = f.getname_w(nameindex) w_obj = f.valuestack.pop() @@ -549,17 +649,17 @@ cmp_is_not, cmp_exc_match, ] - def COMPARE_OP(f, testnum): + def COMPARE_OP(f, testnum, *ignored): w_2 = f.valuestack.pop() w_1 = f.valuestack.pop() try: testfn = f.compare_dispatch_table[testnum] except IndexError: - raise pyframe.BytecodeCorruption, "bad COMPARE_OP oparg" + raise BytecodeCorruption, "bad COMPARE_OP oparg" w_result = testfn(f, w_1, w_2) f.valuestack.push(w_result) - def IMPORT_NAME(f, nameindex): + def IMPORT_NAME(f, nameindex, *ignored): space = f.space w_modulename = f.getname_w(nameindex) modulename = f.space.str_w(w_modulename) @@ -575,13 +675,13 @@ f.w_globals, w_locals, w_fromlist) f.valuestack.push(w_obj) - def IMPORT_STAR(f): + def IMPORT_STAR(f, *ignored): w_module = f.valuestack.pop() w_locals = f.getdictscope() import_all_from(f.space, w_module, w_locals) f.setdictscope(w_locals) - def IMPORT_FROM(f, nameindex): + def IMPORT_FROM(f, nameindex, *ignored): w_name = f.getname_w(nameindex) w_module = f.valuestack.top() try: @@ -593,29 +693,31 @@ f.space.wrap("cannot import name '%s'" % f.space.str_w(w_name) )) f.valuestack.push(w_obj) - def JUMP_FORWARD(f, stepby): - f.next_instr += stepby - JUMP_FORWARD.can_jump = True + def JUMP_FORWARD(f, jumpby, next_instr, *ignored): + next_instr += jumpby + return next_instr - def JUMP_IF_FALSE(f, stepby): + def JUMP_IF_FALSE(f, stepby, next_instr, *ignored): w_cond = f.valuestack.top() if not f.space.is_true(w_cond): - f.next_instr += stepby + next_instr += stepby + return next_instr - def JUMP_IF_TRUE(f, stepby): + def JUMP_IF_TRUE(f, stepby, next_instr, *ignored): w_cond = f.valuestack.top() if f.space.is_true(w_cond): - f.next_instr += stepby + next_instr += stepby + return next_instr - def JUMP_ABSOLUTE(f, jumpto): - f.next_instr = jumpto + def JUMP_ABSOLUTE(f, jumpto, next_instr, *ignored): + return jumpto - def GET_ITER(f): + def GET_ITER(f, *ignored): w_iterable = f.valuestack.pop() w_iterator = f.space.iter(w_iterable) f.valuestack.push(w_iterator) - def FOR_ITER(f, jumpby): + def FOR_ITER(f, jumpby, next_instr, *ignored): w_iterator = f.valuestack.top() try: w_nextitem = f.space.next(w_iterator) @@ -624,33 +726,33 @@ raise # iterator exhausted f.valuestack.pop() - f.next_instr += jumpby + next_instr += jumpby else: f.valuestack.push(w_nextitem) + return next_instr - def FOR_LOOP(f, oparg): - raise pyframe.BytecodeCorruption, "old opcode, no longer in use" + def FOR_LOOP(f, oparg, *ignored): + raise BytecodeCorruption, "old opcode, no longer in use" - def SETUP_LOOP(f, offsettoend): - block = pyframe.LoopBlock(f, f.next_instr + offsettoend) + def SETUP_LOOP(f, offsettoend, next_instr, *ignored): + block = LoopBlock(f, next_instr + offsettoend) f.blockstack.push(block) - def SETUP_EXCEPT(f, offsettoend): - block = pyframe.ExceptBlock(f, f.next_instr + offsettoend) + def SETUP_EXCEPT(f, offsettoend, next_instr, *ignored): + block = ExceptBlock(f, next_instr + offsettoend) f.blockstack.push(block) - def SETUP_FINALLY(f, offsettoend): - block = pyframe.FinallyBlock(f, f.next_instr + offsettoend) + def SETUP_FINALLY(f, offsettoend, next_instr, *ignored): + block = FinallyBlock(f, next_instr + offsettoend) f.blockstack.push(block) - def WITH_CLEANUP(f): + def WITH_CLEANUP(f, *ignored): # see comment in END_FINALLY for stack state w_exitfunc = f.valuestack.pop() w_unroller = f.valuestack.top(2) unroller = f.space.interpclass_w(w_unroller) - if (isinstance(unroller, pyframe.SuspendedUnroller) - and isinstance(unroller.flowexc, pyframe.SApplicationException)): - operr = unroller.flowexc.operr + if isinstance(unroller, SApplicationException): + operr = unroller.operr w_result = f.space.call_function(w_exitfunc, operr.w_type, operr.w_value, @@ -684,7 +786,7 @@ rstack.resume_point("call_function", f, returns=w_result) f.valuestack.push(w_result) - def CALL_FUNCTION(f, oparg): + def CALL_FUNCTION(f, oparg, *ignored): # XXX start of hack for performance if (oparg >> 8) & 0xff == 0: # Only positional arguments @@ -701,20 +803,20 @@ # general case f.call_function(oparg) - def CALL_FUNCTION_VAR(f, oparg): + def CALL_FUNCTION_VAR(f, oparg, *ignored): w_varargs = f.valuestack.pop() f.call_function(oparg, w_varargs) - def CALL_FUNCTION_KW(f, oparg): + def CALL_FUNCTION_KW(f, oparg, *ignored): w_varkw = f.valuestack.pop() f.call_function(oparg, None, w_varkw) - def CALL_FUNCTION_VAR_KW(f, oparg): + def CALL_FUNCTION_VAR_KW(f, oparg, *ignored): w_varkw = f.valuestack.pop() w_varargs = f.valuestack.pop() f.call_function(oparg, w_varargs, w_varkw) - def MAKE_FUNCTION(f, numdefaults): + def MAKE_FUNCTION(f, numdefaults, *ignored): w_codeobj = f.valuestack.pop() codeobj = f.space.interp_w(PyCode, w_codeobj) defaultarguments = [f.valuestack.pop() for i in range(numdefaults)] @@ -722,169 +824,244 @@ fn = function.Function(f.space, codeobj, f.w_globals, defaultarguments) f.valuestack.push(f.space.wrap(fn)) - def BUILD_SLICE(f, numargs): + def BUILD_SLICE(f, numargs, *ignored): if numargs == 3: w_step = f.valuestack.pop() elif numargs == 2: w_step = f.space.w_None else: - raise pyframe.BytecodeCorruption + raise BytecodeCorruption w_end = f.valuestack.pop() w_start = f.valuestack.pop() w_slice = f.space.newslice(w_start, w_end, w_step) f.valuestack.push(w_slice) - def LIST_APPEND(f): + def LIST_APPEND(f, *ignored): w = f.valuestack.pop() v = f.valuestack.pop() f.space.call_method(v, 'append', w) - def SET_LINENO(f, lineno): + def SET_LINENO(f, lineno, *ignored): pass -## def EXTENDED_ARG(f, oparg): +## def EXTENDED_ARG(f, oparg, *ignored): ## opcode = f.nextop() ## oparg = oparg<<16 | f.nextarg() ## fn = f.dispatch_table_w_arg[opcode] ## if fn is None: -## raise pyframe.BytecodeCorruption +## raise BytecodeCorruption ## fn(f, oparg) - def MISSING_OPCODE(f): - ofs = f.next_instr - 1 + def MISSING_OPCODE(f, oparg, next_instr, *ignored): + ofs = next_instr - 1 c = f.pycode.co_code[ofs] name = f.pycode.co_name - raise pyframe.BytecodeCorruption("unknown opcode, ofs=%d, code=%d, name=%s" % - (ofs, ord(c), name) ) - - def MISSING_OPCODE_W_ARG(f, oparg): - ofs = f.next_instr - 3 - c = f.pycode.co_code[ofs] - name = f.pycode.co_name - raise pyframe.BytecodeCorruption("unknown opcode, ofs=%d, code=%d, name=%s" % - (ofs, ord(c), name) ) + raise BytecodeCorruption("unknown opcode, ofs=%d, code=%d, name=%s" % + (ofs, ord(c), name) ) STOP_CODE = MISSING_OPCODE - ### dispatch_table ### - - # 'opcode_has_arg' is a class attribute: list of True/False whether opcode takes arg - # 'dispatch_table_no_arg: list of functions/None - # 'dispatch_table_w_arg: list of functions/None - - __metaclass__ = InitializedClass - def __initclass__(cls): - "NOT_RPYTHON" - # create the 'cls.dispatch_table' attribute - opcode_has_arg = [] - dispatch_table_no_arg = [] - dispatch_table_w_arg = [] - missing_opcode = cls.MISSING_OPCODE.im_func - missing_opcode_w_arg = cls.MISSING_OPCODE_W_ARG.im_func - for i in range(256): - opname = pythonopcode.opname[i].replace('+', '_') - fn = getattr(cls, opname, None) - fn = getattr(fn, 'im_func',fn) - has_arg = i >= pythonopcode.HAVE_ARGUMENT - #if fn is missing_opcode and not opname.startswith('<') and i>0: - # import warnings - # warnings.warn("* Warning, missing opcode %s" % opname) - opcode_has_arg.append(has_arg) - if has_arg: - fn = fn or missing_opcode_w_arg - dispatch_table_w_arg.append(fn) - dispatch_table_no_arg.append(None) - else: - fn = fn or missing_opcode - dispatch_table_no_arg.append(fn) - dispatch_table_w_arg.append(None) - - cls.opcode_has_arg = opcode_has_arg - cls.dispatch_table_no_arg = dispatch_table_no_arg - cls.dispatch_table_w_arg = dispatch_table_w_arg - - #XXX performance hack! - ### Create dispatch with a lot of if,elifs ### - ### (this gets optimized for translated pypy by the merge_if_blocks transformation) ### - if cls.__name__ != 'PyInterpFrame': - return - import py - - dispatch_code = ''' -def dispatch_translated(self, code, ec): - hint(None, global_merge_point=True) - next_instr = hint(self.next_instr, promote=True) - while True: - hint(None, global_merge_point=True) - self.last_instr = intmask(next_instr) - #ec.bytecode_trace(self) JJJ - #self.next_instr = self.last_instr JJJ - opcode = ord(code[next_instr]) - opcode = hint(opcode, concrete=True) - if self.space.config.objspace.logbytecodes: - self.space.bytecodecounts[opcode] = self.space.bytecodecounts.get(opcode, 0) + 1 - next_instr += 1 - self.next_instr = next_instr - if opcode >= %s: - oparg = ord(code[next_instr]) | ord(code[next_instr + 1]) << 8 - oparg = hint(oparg, concrete=True) - next_instr += 2 - self.next_instr = next_instr - while True: - if opcode == %s: - opcode = ord(code[next_instr]) - opcode = hint(opcode, concrete=True) - oparg = oparg << 16 | ord(code[next_instr + 1]) | ord(code[next_instr + 2]) << 8 - oparg = hint(oparg, concrete=True) - next_instr += 3 - self.next_instr = next_instr - if opcode < %s: - raise pyframe.BytecodeCorruption - continue -''' % (pythonopcode.HAVE_ARGUMENT, - pythonopcode.EXTENDED_ARG, - pythonopcode.HAVE_ARGUMENT) - - def sortkey(opcode, opcodeorder=opcodeorder, ValueError=ValueError): - try: - index = opcodeorder.index(opcode) - except ValueError: - index = 1000000 - return index, opcode - opcases = [(sortkey(i), i, opname) - for opname, i in pythonopcode.opmap.iteritems()] - opcases.sort() # for predictable results - for _, i, opname in opcases: - if i == pythonopcode.EXTENDED_ARG or i < pythonopcode.HAVE_ARGUMENT: - continue - opname = opname.replace('+', '_') - dispatch_code += ' elif opcode == %d:\n' % i - dispatch_code += ' self.%s(oparg)\n' % opname - if opname == 'CALL_FUNCTION': - dispatch_code += ' rstack.resume_point("dispatch_call", self, code, ec)\n' - dispatch_code += ' else:\n' - dispatch_code += ' self.MISSING_OPCODE_W_ARG(oparg)\n' - dispatch_code += ' break\n' +### ____________________________________________________________ ### - for _, i, opname in opcases: - if i >= pythonopcode.HAVE_ARGUMENT: - continue - opname = opname.replace('+', '_') - dispatch_code += ' elif opcode == %d:\n' % i - dispatch_code += ' self.%s()\n' % opname - dispatch_code += ' else:\n' - dispatch_code += ' self.MISSING_OPCODE()\n' - exec py.code.Source(dispatch_code).compile() - cls.dispatch_translated = dispatch_translated - +def cpython_tb(): + """NOT_RPYTHON""" + import sys + return sys.exc_info()[2] +cpython_tb._annspecialcase_ = "override:ignore" + +class Reraise(Exception): + """Signal an application-level OperationError that should not grow + a new traceback entry nor trigger the trace hook.""" + +class BytecodeCorruption(Exception): + """Detected bytecode corruption. Never caught; it's an error.""" + + +### Frame Blocks ### + +class SuspendedUnroller(Wrappable): + """Abstract base class for interpreter-level objects that + instruct the interpreter to change the control flow and the + block stack. + + The concrete subclasses correspond to the various values WHY_XXX + values of the why_code enumeration in ceval.c: + + WHY_NOT, OK, not this one :-) + WHY_EXCEPTION, SApplicationException + WHY_RERAISE, implemented differently, see Reraise + WHY_RETURN, SReturnValue + WHY_BREAK, SBreakLoop + WHY_CONTINUE, SContinueLoop + WHY_YIELD not needed + """ + def nomoreblocks(self): + raise BytecodeCorruption("misplaced bytecode - should not return") + # for the flow object space, a way to "pickle" and "unpickle" the + # ControlFlowException by enumerating the Variables it contains. + def state_unpack_variables(self, space): + return [] # by default, overridden below + def state_pack_variables(self, space, *values_w): + assert len(values_w) == 0 + +class SReturnValue(SuspendedUnroller): + """Signals a 'return' statement. + Argument is the wrapped object to return.""" + kind = 0x01 + def __init__(self, w_returnvalue): + self.w_returnvalue = w_returnvalue + def nomoreblocks(self): + return self.w_returnvalue + def state_unpack_variables(self, space): + return [self.w_returnvalue] + def state_pack_variables(self, space, w_returnvalue): + self.w_returnvalue = w_returnvalue + +class SApplicationException(SuspendedUnroller): + """Signals an application-level exception + (i.e. an OperationException).""" + kind = 0x02 + def __init__(self, operr): + self.operr = operr + def nomoreblocks(self): + raise self.operr + def state_unpack_variables(self, space): + return [self.operr.w_type, self.operr.w_value] + def state_pack_variables(self, space, w_type, w_value): + self.operr = OperationError(w_type, w_value) + +class SBreakLoop(SuspendedUnroller): + """Signals a 'break' statement.""" + kind = 0x04 +SBreakLoop.singleton = SBreakLoop() + +class SContinueLoop(SuspendedUnroller): + """Signals a 'continue' statement. + Argument is the bytecode position of the beginning of the loop.""" + kind = 0x08 + def __init__(self, jump_to): + self.jump_to = jump_to + def state_unpack_variables(self, space): + return [space.wrap(self.jump_to)] + def state_pack_variables(self, space, w_jump_to): + self.jump_to = space.int_w(w_jump_to) + + +class FrameBlock: + + """Abstract base class for frame blocks from the blockstack, + used by the SETUP_XXX and POP_BLOCK opcodes.""" + + def __init__(self, frame, handlerposition): + self.handlerposition = handlerposition + self.valuestackdepth = frame.valuestack.depth() + + def __eq__(self, other): + return (self.__class__ is other.__class__ and + self.handlerposition == other.handlerposition and + self.valuestackdepth == other.valuestackdepth) + + def __ne__(self, other): + return not (self == other) + + def __hash__(self): + return hash((self.handlerposition, self.valuestackdepth)) + + def cleanupstack(self, frame): + for i in range(self.valuestackdepth, frame.valuestack.depth()): + frame.valuestack.pop() + + def cleanup(self, frame): + "Clean up a frame when we normally exit the block." + self.cleanupstack(frame) + + # internal pickling interface, not using the standard protocol + def _get_state_(self, space): + w = space.wrap + return space.newtuple([w(self._opname), w(self.handlerposition), + w(self.valuestackdepth)]) + +class LoopBlock(FrameBlock): + """A loop block. Stores the end-of-loop pointer in case of 'break'.""" + + _opname = 'SETUP_LOOP' + handling_mask = SBreakLoop.kind | SContinueLoop.kind + + def handle(self, frame, unroller): + if isinstance(unroller, SContinueLoop): + # re-push the loop block without cleaning up the value stack, + # and jump to the beginning of the loop, stored in the + # exception's argument + frame.blockstack.push(self) + return unroller.jump_to + else: + # jump to the end of the loop + self.cleanupstack(frame) + return self.handlerposition + + +class ExceptBlock(FrameBlock): + """An try:except: block. Stores the position of the exception handler.""" + + _opname = 'SETUP_EXCEPT' + handling_mask = SApplicationException.kind + + def handle(self, frame, unroller): + # push the exception to the value stack for inspection by the + # exception handler (the code after the except:) + self.cleanupstack(frame) + assert isinstance(unroller, SApplicationException) + operationerr = unroller.operr + if frame.space.full_exceptions: + operationerr.normalize_exception(frame.space) + # the stack setup is slightly different than in CPython: + # instead of the traceback, we store the unroller object, + # wrapped. + frame.valuestack.push(frame.space.wrap(unroller)) + frame.valuestack.push(operationerr.w_value) + frame.valuestack.push(operationerr.w_type) + return self.handlerposition # jump to the handler + + +class FinallyBlock(FrameBlock): + """A try:finally: block. Stores the position of the exception handler.""" + + _opname = 'SETUP_FINALLY' + handling_mask = -1 # handles every kind of SuspendedUnroller + + def cleanup(self, frame): + # upon normal entry into the finally: part, the standard Python + # bytecode pushes a single None for END_FINALLY. In our case we + # always push three values into the stack: the wrapped ctlflowexc, + # the exception value and the exception type (which are all None + # here). + self.cleanupstack(frame) + # one None already pushed by the bytecode + frame.valuestack.push(frame.space.w_None) + frame.valuestack.push(frame.space.w_None) + + def handle(self, frame, unroller): + # any abnormal reason for unrolling a finally: triggers the end of + # the block unrolling and the entering the finally: handler. + # see comments in cleanup(). + self.cleanupstack(frame) + frame.valuestack.push(frame.space.wrap(unroller)) + frame.valuestack.push(frame.space.w_None) + frame.valuestack.push(frame.space.w_None) + return self.handlerposition # jump to the handler + + +block_classes = {'SETUP_LOOP': LoopBlock, + 'SETUP_EXCEPT': ExceptBlock, + 'SETUP_FINALLY': FinallyBlock} ### helpers written at the application-level ### # Some of these functions are expected to be generally useful if other # parts of the code need to do the same thing as a non-trivial opcode, # like finding out which metaclass a new class should have. -# This is why they are not methods of PyInterpFrame. +# This is why they are not methods of PyFrame. # There are also a couple of helpers that are methods, defined in the # class above. Modified: pypy/branch/jit-real-world/pypy/interpreter/test/test_interpreter.py ============================================================================== --- pypy/branch/jit-real-world/pypy/interpreter/test/test_interpreter.py (original) +++ pypy/branch/jit-real-world/pypy/interpreter/test/test_interpreter.py Fri Dec 1 03:43:34 2006 @@ -150,7 +150,7 @@ 1+2+3 + 5+6+7+8+900) def test_import(self): - # Regression test for a bug in PyInterpFrame.IMPORT_NAME: when an + # Regression test for a bug in PyFrame.IMPORT_NAME: when an # import statement was executed in a function without a locals dict, a # plain unwrapped None could be passed into space.call_function causing # assertion errors later on. Modified: pypy/branch/jit-real-world/pypy/interpreter/test/test_pyframe.py ============================================================================== --- pypy/branch/jit-real-world/pypy/interpreter/test/test_pyframe.py (original) +++ pypy/branch/jit-real-world/pypy/interpreter/test/test_pyframe.py Fri Dec 1 03:43:34 2006 @@ -94,6 +94,53 @@ assert len(l) == 1 assert isinstance(l[0][1], Exception) + def test_dont_trace_on_reraise(self): + import sys + l = [] + def ltrace(a,b,c): + if b == 'exception': + l.append(c) + return ltrace + def trace(a,b,c): return ltrace + def f(): + try: + 1/0 + except: + try: + raise + except: + pass + sys.settrace(trace) + f() + sys.settrace(None) + assert len(l) == 1 + assert issubclass(l[0][0], Exception) + + def test_dont_trace_on_raise_with_tb(self): + import sys + l = [] + def ltrace(a,b,c): + if b == 'exception': + l.append(c) + return ltrace + def trace(a,b,c): return ltrace + def f(): + try: + raise Exception + except: + return sys.exc_info() + def g(): + exc, val, tb = f() + try: + raise exc, val, tb + except: + pass + sys.settrace(trace) + g() + sys.settrace(None) + assert len(l) == 1 + assert isinstance(l[0][1], Exception) + def test_trace_changes_locals(self): import sys def trace(frame, what, arg): Modified: pypy/branch/jit-real-world/pypy/interpreter/typedef.py ============================================================================== --- pypy/branch/jit-real-world/pypy/interpreter/typedef.py (original) +++ pypy/branch/jit-real-world/pypy/interpreter/typedef.py Fri Dec 1 03:43:34 2006 @@ -442,7 +442,8 @@ from pypy.interpreter.eval import Code, Frame from pypy.interpreter.pycode import PyCode, CO_VARARGS, CO_VARKEYWORDS -from pypy.interpreter.pyframe import PyFrame, ControlFlowException +from pypy.interpreter.pyframe import PyFrame +from pypy.interpreter.pyopcode import SuspendedUnroller from pypy.interpreter.module import Module from pypy.interpreter.function import Function, Method, StaticMethod from pypy.interpreter.function import BuiltinFunction, descr_function_get @@ -696,7 +697,7 @@ __repr__ = interp2app(NotImplemented.descr__repr__), ) -ControlFlowException.typedef = TypeDef("ControlFlowException") +SuspendedUnroller.typedef = TypeDef("SuspendedUnroller") interptypes = [ val.typedef for name,val in globals().items() if hasattr(val,'__bases__') and hasattr(val,'typedef') ] Modified: pypy/branch/jit-real-world/pypy/module/_pickle_support/maker.py ============================================================================== --- pypy/branch/jit-real-world/pypy/module/_pickle_support/maker.py (original) +++ pypy/branch/jit-real-world/pypy/module/_pickle_support/maker.py Fri Dec 1 03:43:34 2006 @@ -61,11 +61,7 @@ w_pycode, = args_w pycode = space.interp_w(PyCode, w_pycode) w = space.wrap - - # let the code object create the right kind of frame - # the distinction is a little over-done but computable - Klass = pycode.get_frame_class() - new_frame = instantiate(Klass) + new_frame = instantiate(PyFrame) return space.wrap(new_frame) frame_new.unwrap_spec = [ObjSpace, Arguments] @@ -76,13 +72,11 @@ def generator_new(space, __args__): args_w, kwds_w = __args__.unpack() #stolen from std/fake.py - w_frame, w_running, w_exhausted = args_w + w_frame, w_running = args_w frame = space.interp_w(PyFrame, w_frame) running = space.int_w(w_running) - exhausted = space.int_w(w_exhausted) new_generator = GeneratorIterator(frame) new_generator.running = running - new_generator.exhausted = exhausted return space.wrap(new_generator) generator_new.unwrap_spec = [ObjSpace, Arguments] Modified: pypy/branch/jit-real-world/pypy/objspace/flow/flowcontext.py ============================================================================== --- pypy/branch/jit-real-world/pypy/objspace/flow/flowcontext.py (original) +++ pypy/branch/jit-real-world/pypy/objspace/flow/flowcontext.py Fri Dec 1 03:43:34 2006 @@ -95,7 +95,7 @@ def bytecode_trace(self, ec, frame): assert frame is ec.crnt_frame, "seeing an unexpected frame!" - ec.crnt_offset = frame.next_instr # save offset for opcode + ec.crnt_offset = frame.last_instr # save offset for opcode if self.enterspamblock: # If we have a SpamBlock, the first call to bytecode_trace() # occurs as soon as frame.resume() starts, before interpretation @@ -218,8 +218,10 @@ # create an empty frame suitable for the code object # while ignoring any operation like the creation of the locals dict self.recorder = [] - return self.code.create_frame(self.space, self.w_globals, - self.closure) + frame = self.code.create_frame(self.space, self.w_globals, + self.closure) + frame.last_instr = 0 + return frame def bytecode_trace(self, frame): self.recorder.bytecode_trace(self, frame) @@ -256,11 +258,15 @@ except StopFlowing: continue # restarting a dead SpamBlock try: + self.framestack.push(frame) self.crnt_frame = frame try: - w_result = frame.resume() + w_result = frame.dispatch(frame.pycode.co_code, + frame.last_instr, + self) finally: self.crnt_frame = None + self.framestack.pop() except OperationThatShouldNotBePropagatedError, e: raise Exception( Modified: pypy/branch/jit-real-world/pypy/objspace/flow/framestate.py ============================================================================== --- pypy/branch/jit-real-world/pypy/objspace/flow/framestate.py (original) +++ pypy/branch/jit-real-world/pypy/objspace/flow/framestate.py Fri Dec 1 03:43:34 2006 @@ -1,4 +1,5 @@ -from pypy.interpreter.pyframe import PyFrame, SuspendedUnroller +from pypy.interpreter.pyframe import PyFrame +from pypy.interpreter.pyopcode import SuspendedUnroller from pypy.interpreter.error import OperationError from pypy.rlib.objectmodel import instantiate from pypy.rlib.unroll import SpecTag @@ -21,7 +22,7 @@ self.mergeable = data self.nonmergeable = ( state.blockstack.items[:], - state.next_instr, + state.last_instr, # == next_instr when between bytecodes state.w_locals, ) elif isinstance(state, tuple): @@ -48,7 +49,7 @@ frame.last_exception = OperationError(data[-2], data[-1]) ( frame.blockstack.items[:], - frame.next_instr, + frame.last_instr, frame.w_locals, ) = self.nonmergeable else: @@ -157,9 +158,9 @@ isinstance(item.value, SuspendedUnroller)): i += 1 else: - flowexc = item.value.flowexc - vars = flowexc.state_unpack_variables(space) - key = flowexc.__class__, len(vars) + unroller = item.value + vars = unroller.state_unpack_variables(space) + key = unroller.__class__, len(vars) try: tag = PICKLE_TAGS[key] except: @@ -171,9 +172,9 @@ for i in range(len(lst)-1, -1, -1): item = lst[i] if item in UNPICKLE_TAGS: - flowexcclass, argcount = UNPICKLE_TAGS[item] + unrollerclass, argcount = UNPICKLE_TAGS[item] arguments = lst[i+1: i+1+argcount] del lst[i+1: i+1+argcount] - flowexc = instantiate(flowexcclass) - flowexc.state_pack_variables(space, *arguments) - lst[i] = flowexc.wrap(space) + unroller = instantiate(unrollerclass) + unroller.state_pack_variables(space, *arguments) + lst[i] = space.wrap(unroller) Modified: pypy/branch/jit-real-world/pypy/tool/stdlib_opcode.py ============================================================================== --- pypy/branch/jit-real-world/pypy/tool/stdlib_opcode.py (original) +++ pypy/branch/jit-real-world/pypy/tool/stdlib_opcode.py Fri Dec 1 03:43:34 2006 @@ -1,9 +1,76 @@ # load opcode.py as pythonopcode from our own lib -# This should handle missing local copy + +__all__ = ['opmap', 'opname', 'HAVE_ARGUMENT', + 'hasjrel', 'hasjabs', 'cmp_op'] + def load_opcode(): import py opcode_path = py.path.local(__file__).dirpath().dirpath().dirpath('lib-python/modified-2.4.1/opcode.py') - execfile(str(opcode_path), globals()) + d = {} + execfile(str(opcode_path), d) + return d -load_opcode() +opcode_dict = load_opcode() del load_opcode + +# copy some stuff from opcode.py directly into our globals +for name in __all__: + if name in opcode_dict: + globals()[name] = opcode_dict[name] + +opcode_method_names = ['MISSING_OPCODE'] * 256 +for name, index in opmap.items(): + opcode_method_names[index] = name.replace('+', '_') + +# ____________________________________________________________ +# RPython-friendly helpers and structures + +from pypy.rlib.unroll import unrolling_iterable + + +class OpcodeDesc(object): + def __init__(self, name, index): + self.name = name + self.methodname = opcode_method_names[index] + self.index = index + self.hasarg = index >= HAVE_ARGUMENT + + def _freeze_(self): + return True + + def is_enabled(self, space): + """Check if the opcode should be enabled in the space's configuration. + (Returns True for all standard opcodes.)""" + opt = space.config.objspace.opcodes + return getattr(opt, self.name, True) + is_enabled._annspecialcase_ = 'specialize:memo' + + # for predictable results, we try to order opcodes most-used-first + opcodeorder = [124, 125, 100, 105, 1, 131, 116, 111, 106, 83, 23, 93, 113, 25, 95, 64, 112, 66, 102, 110, 60, 92, 62, 120, 68, 87, 32, 136, 4, 103, 24, 63, 18, 65, 15, 55, 121, 3, 101, 22, 12, 80, 86, 135, 126, 90, 140, 104, 2, 33, 20, 108, 107, 31, 134, 132, 88, 30, 133, 130, 137, 141, 61, 122, 11, 40, 74, 73, 51, 96, 21, 42, 56, 85, 82, 89, 142, 77, 78, 79, 91, 76, 97, 57, 19, 43, 84, 50, 41, 99, 53, 26] + + def sortkey(self): + try: + i = self.opcodeorder.index(self.index) + except ValueError: + i = 1000000 + return i, self.index + + def __cmp__(self, other): + return cmp(self.sortkey(), other.sortkey()) + + +opdescmap = {} + +class opcodedesc: + """A namespace mapping OPCODE_NAME to OpcodeDescs.""" + +for name, index in opmap.items(): + desc = OpcodeDesc(name, index) + setattr(opcodedesc, name, desc) + opdescmap[index] = desc + +lst = opdescmap.values() +lst.sort() +unrolling_opcode_descs = unrolling_iterable(lst) + +del name, index, desc, lst From fijal at codespeak.net Fri Dec 1 10:46:50 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 1 Dec 2006 10:46:50 +0100 (CET) Subject: [pypy-svn] r35188 - pypy/dist/pypy/module/thread/rpython/test Message-ID: <20061201094650.13BD310068@code0.codespeak.net> Author: fijal Date: Fri Dec 1 10:46:47 2006 New Revision: 35188 Modified: pypy/dist/pypy/module/thread/rpython/test/test_ll_thread.py Log: Added a skip when threads are not there. Modified: pypy/dist/pypy/module/thread/rpython/test/test_ll_thread.py ============================================================================== --- pypy/dist/pypy/module/thread/rpython/test/test_ll_thread.py (original) +++ pypy/dist/pypy/module/thread/rpython/test/test_ll_thread.py Fri Dec 1 10:46:47 2006 @@ -1,4 +1,7 @@ -import thread +try: + import thread +except ImportError: + py.test.skip("Threads not supported") import pypy.module.thread.rpython.exttable # for declare()/declaretype() from pypy.module.thread.rpython.ll_thread import * from pypy.annotation.annrpython import RPythonAnnotator From fijal at codespeak.net Fri Dec 1 10:48:19 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 1 Dec 2006 10:48:19 +0100 (CET) Subject: [pypy-svn] r35189 - in pypy/dist/pypy/module/posix: . test Message-ID: <20061201094819.4005D1006C@code0.codespeak.net> Author: fijal Date: Fri Dec 1 10:48:17 2006 New Revision: 35189 Modified: pypy/dist/pypy/module/posix/__init__.py pypy/dist/pypy/module/posix/interp_posix.py pypy/dist/pypy/module/posix/test/test_posix2.py Log: Added (module-level) os.execv Modified: pypy/dist/pypy/module/posix/__init__.py ============================================================================== --- pypy/dist/pypy/module/posix/__init__.py (original) +++ pypy/dist/pypy/module/posix/__init__.py Fri Dec 1 10:48:17 2006 @@ -70,6 +70,8 @@ interpleveldefs['fork'] = 'interp_posix.fork' if hasattr(os, 'waitpid'): interpleveldefs['waitpid'] = 'interp_posix.waitpid' + if hasattr(os, 'execv'): + interpleveldefs['execv'] = 'interp_posix.execv' #if hasattr(ctypes_posix, 'uname'): # interpleveldefs['uname'] = 'interp_posix.uname' Modified: pypy/dist/pypy/module/posix/interp_posix.py ============================================================================== --- pypy/dist/pypy/module/posix/interp_posix.py (original) +++ pypy/dist/pypy/module/posix/interp_posix.py Fri Dec 1 10:48:17 2006 @@ -1,4 +1,4 @@ -from pypy.interpreter.baseobjspace import ObjSpace +from pypy.interpreter.baseobjspace import ObjSpace, W_Root from pypy.rlib.rarithmetic import intmask from pypy.rlib import ros from pypy.interpreter.error import OperationError @@ -398,6 +398,10 @@ return space.wrap(intmask(_c.geteuid())) geteuid.unwrap_spec = [ObjSpace] +def execv(space, command, w_args): + os.execv(command, [space.str_w(i) for i in space.unpackiterable(w_args)]) +execv.unwrap_spec = [ObjSpace, str, W_Root] + def uname(space): try: result = _c.uname() Modified: pypy/dist/pypy/module/posix/test/test_posix2.py ============================================================================== --- pypy/dist/pypy/module/posix/test/test_posix2.py (original) +++ pypy/dist/pypy/module/posix/test/test_posix2.py Fri Dec 1 10:48:17 2006 @@ -101,6 +101,16 @@ assert pid1 == pid # XXX check status1 + if hasattr(__import__(os.name), "execv"): # and fork + def test_execv(self): + os = self.posix + pid = os.fork() + if pid == 0: + os.execv("/usr/bin/env", ["env", "python", "-c", "open('onefile', 'w').write('1')"]) + os.waitpid(pid, 0) + assert open("onefile").read() == "1" + os.unlink("onefile") + class AppTestEnvironment(object): def setup_class(cls): cls.space = space From fijal at codespeak.net Fri Dec 1 10:49:46 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 1 Dec 2006 10:49:46 +0100 (CET) Subject: [pypy-svn] r35190 - in pypy/dist/pypy: rpython rpython/module translator/c translator/c/src translator/c/test Message-ID: <20061201094946.79A6310071@code0.codespeak.net> Author: fijal Date: Fri Dec 1 10:49:43 2006 New Revision: 35190 Modified: pypy/dist/pypy/rpython/extfunctable.py pypy/dist/pypy/rpython/module/ll_os.py pypy/dist/pypy/translator/c/extfunc.py pypy/dist/pypy/translator/c/src/ll_os.h pypy/dist/pypy/translator/c/test/test_extfunc.py Log: Added os.execv Modified: pypy/dist/pypy/rpython/extfunctable.py ============================================================================== --- pypy/dist/pypy/rpython/extfunctable.py (original) +++ pypy/dist/pypy/rpython/extfunctable.py Fri Dec 1 10:49:43 2006 @@ -233,6 +233,8 @@ declare(os.spawnv, int, 'll_os/spawnv') if hasattr(os, 'waitpid'): declare(os.waitpid , waitpidannotation, 'll_os/waitpid') +if hasattr(os, 'execv'): + declare(os.execv, noneannotation, 'll_os/execv') declare(os.path.exists, bool , 'll_os_path/exists') declare(os.path.isdir, bool , 'll_os_path/isdir') Modified: pypy/dist/pypy/rpython/module/ll_os.py ============================================================================== --- pypy/dist/pypy/rpython/module/ll_os.py (original) +++ pypy/dist/pypy/rpython/module/ll_os.py Fri Dec 1 10:49:43 2006 @@ -96,6 +96,10 @@ return os.system(cls.from_rstr(cmd)) ll_os_system.suggested_primitive = True + def ll_os_execv(cls, cmd, args): + os.execv(cmd, args) + ll_os_execv.suggested_primitive = True + def ll_os_unlink(cls, path): os.unlink(cls.from_rstr(path)) ll_os_unlink.suggested_primitive = True Modified: pypy/dist/pypy/translator/c/extfunc.py ============================================================================== --- pypy/dist/pypy/translator/c/extfunc.py (original) +++ pypy/dist/pypy/translator/c/extfunc.py Fri Dec 1 10:49:43 2006 @@ -60,6 +60,7 @@ impl.ll_os_spawnv.im_func: 'LL_os_spawnv', impl.ll_os_waitpid.im_func: 'LL_os_waitpid', impl.ll_os__exit.im_func: 'LL_os__exit', + impl.ll_os_execv.im_func: 'LL_os_execv', ll_time.ll_time_clock: 'LL_time_clock', ll_time.ll_time_sleep: 'LL_time_sleep', ll_time.ll_time_time: 'LL_time_time', Modified: pypy/dist/pypy/translator/c/src/ll_os.h ============================================================================== --- pypy/dist/pypy/translator/c/src/ll_os.h (original) +++ pypy/dist/pypy/translator/c/src/ll_os.h Fri Dec 1 10:49:43 2006 @@ -80,6 +80,7 @@ long LL_readlink_into(RPyString *path, RPyString *buffer); long LL_os_fork(void); #ifdef HAVE_RPY_LIST_OF_STRING /* argh */ +void LL_os_execv(RPyString *cmd, RPyListOfString *args); long LL_os_spawnv(int mode, RPyString *path, RPyListOfString *args); #endif RPyWAITPID_RESULT* LL_os_waitpid(long pid, long options); @@ -389,6 +390,20 @@ } #endif +#if defined(HAVE_EXECV) && defined(HAVE_RPY_LIST_OF_STRING) +void LL_os_execv(RPyString *cmd, RPyListOfString *args) { + int i, nargs = args->l_length; + char **slist = malloc((nargs+1) * sizeof(char*)); + if (slist) { + for (i=0; il_items->items[i]); + slist[nargs] = NULL; + execv(RPyString_AsString(cmd), slist); + } /* should never return */ + RPYTHON_RAISE_OSERROR(errno); +} +#endif + /* The following code is only generated if spawnv exists and if RPyListOfString exists. The latter is a bit tricky: Modified: pypy/dist/pypy/translator/c/test/test_extfunc.py ============================================================================== --- pypy/dist/pypy/translator/c/test/test_extfunc.py (original) +++ pypy/dist/pypy/translator/c/test/test_extfunc.py Fri Dec 1 10:49:43 2006 @@ -5,6 +5,7 @@ from pypy.translator.c.test.test_genc import compile from pypy.translator.c.extfunc import EXTERNALS from pypy.rlib import ros +from pypy.translator.stackless.test.test_transform import one def test_all_suggested_primitives(): for modulename in ['ll_math', 'll_os', 'll_os_path', 'll_time']: @@ -684,3 +685,20 @@ compared_with.sort() assert result == compared_with +if hasattr(posix, 'execv'): + def test_execv(): + filename = str(udir.join('test_execv.txt')) + def does_stuff(): + progname = str(sys.executable) + l = [] + l.append(progname) + l += ["-c", 'open("%s","w").write("1")' % filename] + pid = os.fork() + if pid == 0: + os.execv(progname, l) + else: + os.waitpid(pid, 0) + func = compile(does_stuff, []) + func() + assert open(filename).read() == "1" + From cfbolz at codespeak.net Fri Dec 1 11:09:10 2006 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Fri, 1 Dec 2006 11:09:10 +0100 (CET) Subject: [pypy-svn] r35191 - pypy/dist/pypy/objspace/std/test Message-ID: <20061201100910.D65FF10071@code0.codespeak.net> Author: cfbolz Date: Fri Dec 1 11:09:09 2006 New Revision: 35191 Modified: pypy/dist/pypy/objspace/std/test/test_userobject.py Log: dict subclassing works just fine now Modified: pypy/dist/pypy/objspace/std/test/test_userobject.py ============================================================================== --- pypy/dist/pypy/objspace/std/test/test_userobject.py (original) +++ pypy/dist/pypy/objspace/std/test/test_userobject.py Fri Dec 1 11:09:09 2006 @@ -25,16 +25,10 @@ def test_subclassing(self): for base in tuple, list, dict, str, int, float: - try: - class subclass(base): pass - stuff = subclass() - except: - print 'not subclassable:', base - if base is not dict: # XXX must be fixed - raise - else: - assert isinstance(stuff, base) - assert subclass.__base__ is base + class subclass(base): pass + stuff = subclass() + assert isinstance(stuff, base) + assert subclass.__base__ is base def test_subclasstuple(self): class subclass(tuple): pass From xoraxax at codespeak.net Fri Dec 1 11:19:43 2006 From: xoraxax at codespeak.net (xoraxax at codespeak.net) Date: Fri, 1 Dec 2006 11:19:43 +0100 (CET) Subject: [pypy-svn] r35192 - pypy/dist/pypy/doc Message-ID: <20061201101943.E5F9C10061@code0.codespeak.net> Author: xoraxax Date: Fri Dec 1 11:19:42 2006 New Revision: 35192 Modified: pypy/dist/pypy/doc/getting-started.txt Log: Added explicit hint to install Boehm. Modified: pypy/dist/pypy/doc/getting-started.txt ============================================================================== --- pypy/dist/pypy/doc/getting-started.txt (original) +++ pypy/dist/pypy/doc/getting-started.txt Fri Dec 1 11:19:42 2006 @@ -528,7 +528,9 @@ By default the translation process will try to use the `Boehm-Demers-Weiser garbage collector`_ for the translated PyPy (Use ``--gc=ref`` to use our own reference counting implementation which -at the moment is slower but doesn't have external dependencies). +at the moment is slower but doesn't have external dependencies). Be sure +to install Boehm before starting the translation (e.g. by running +``apt-get install libgc-dev`` on Debian). This whole process will take some time and quite a lot of memory. To reduce the memory footprint of the translation process you can use the From xoraxax at codespeak.net Fri Dec 1 11:24:21 2006 From: xoraxax at codespeak.net (xoraxax at codespeak.net) Date: Fri, 1 Dec 2006 11:24:21 +0100 (CET) Subject: [pypy-svn] r35193 - pypy/dist/pypy/doc Message-ID: <20061201102421.4259F10071@code0.codespeak.net> Author: xoraxax Date: Fri Dec 1 11:24:19 2006 New Revision: 35193 Modified: pypy/dist/pypy/doc/getting-started.txt Log: Ensured continuity by adding the word "Otherwise". Modified: pypy/dist/pypy/doc/getting-started.txt ============================================================================== --- pypy/dist/pypy/doc/getting-started.txt (original) +++ pypy/dist/pypy/doc/getting-started.txt Fri Dec 1 11:24:19 2006 @@ -528,7 +528,7 @@ By default the translation process will try to use the `Boehm-Demers-Weiser garbage collector`_ for the translated PyPy (Use ``--gc=ref`` to use our own reference counting implementation which -at the moment is slower but doesn't have external dependencies). Be sure +at the moment is slower but doesn't have external dependencies). Otherwise, be sure to install Boehm before starting the translation (e.g. by running ``apt-get install libgc-dev`` on Debian). From arigo at codespeak.net Fri Dec 1 12:41:48 2006 From: arigo at codespeak.net (arigo at codespeak.net) Date: Fri, 1 Dec 2006 12:41:48 +0100 (CET) Subject: [pypy-svn] r35194 - in pypy/dist/pypy: objspace/flow/test rlib Message-ID: <20061201114148.58F2710061@code0.codespeak.net> Author: arigo Date: Fri Dec 1 12:41:45 2006 New Revision: 35194 Modified: pypy/dist/pypy/objspace/flow/test/test_unroll.py pypy/dist/pypy/rlib/unroll.py Log: Fix a combinatorical explosion when unrolling loops with "if" statements. Done by caching the _unroller SpecTags, instead of making fresh ones even for the same indices. Modified: pypy/dist/pypy/objspace/flow/test/test_unroll.py ============================================================================== --- pypy/dist/pypy/objspace/flow/test/test_unroll.py (original) +++ pypy/dist/pypy/objspace/flow/test/test_unroll.py Fri Dec 1 12:41:45 2006 @@ -1,3 +1,4 @@ +import operator from pypy.objspace.flow.test.test_objspace import Base from pypy.rlib.unroll import unrolling_zero, unrolling_iterable @@ -38,3 +39,28 @@ graph = self.codetest(f) ops = self.all_operations(graph) assert ops == {'setattr': 3} + + def test_unroll_ifs(self): + operations = unrolling_iterable([operator.lt, + operator.le, + operator.eq, + operator.ne, + operator.gt, + operator.ge]) + def accept(n): + "stub" + def f(x, y): + for op in operations: + if accept(op): + op(x, y) + + graph = self.codetest(f) + ops = self.all_operations(graph) + assert ops == {'simple_call': 6, + 'is_true': 6, + 'lt': 1, + 'le': 1, + 'eq': 1, + 'ne': 1, + 'gt': 1, + 'ge': 1} Modified: pypy/dist/pypy/rlib/unroll.py ============================================================================== --- pypy/dist/pypy/rlib/unroll.py (original) +++ pypy/dist/pypy/rlib/unroll.py Fri Dec 1 12:41:45 2006 @@ -43,12 +43,13 @@ def __init__(self, iterable): self._items = list(iterable) + self._head = _unroller(self._items) def __iter__(self): return iter(self._items) def get_unroller(self): - return _unroller(self._items) + return self._head class _unroller(SpecTag): @@ -56,8 +57,10 @@ def __init__(self, items, i=0): self._items = items self._i = i + self._next = None def step(self): v = self._items[self._i] - next = _unroller(self._items, self._i+1) - return v, next + if self._next is None: + self._next = _unroller(self._items, self._i+1) + return v, self._next From arigo at codespeak.net Fri Dec 1 12:43:03 2006 From: arigo at codespeak.net (arigo at codespeak.net) Date: Fri, 1 Dec 2006 12:43:03 +0100 (CET) Subject: [pypy-svn] r35195 - pypy/branch/jit-real-world/pypy/interpreter Message-ID: <20061201114303.B54B010068@code0.codespeak.net> Author: arigo Date: Fri Dec 1 12:43:01 2006 New Revision: 35195 Modified: pypy/branch/jit-real-world/pypy/interpreter/pyopcode.py Log: Fix a typo, move a bit of code out of dispatch_bytecode(). Modified: pypy/branch/jit-real-world/pypy/interpreter/pyopcode.py ============================================================================== --- pypy/branch/jit-real-world/pypy/interpreter/pyopcode.py (original) +++ pypy/branch/jit-real-world/pypy/interpreter/pyopcode.py Fri Dec 1 12:43:01 2006 @@ -62,7 +62,7 @@ except MemoryError: next_instr = self.handle_asynchronous_error(ec, self.space.w_MemoryError) - except RuntimeError: + except RuntimeError, e: if we_are_translated(): # stack overflows should be the only kind of RuntimeErrors # in translated PyPy @@ -131,7 +131,6 @@ w_returnvalue = self.valuestack.pop() block = self.unrollstack(SReturnValue.kind) if block is None: - self.frame_finished_execution = True # for generators return w_returnvalue else: unroller = SReturnValue(w_returnvalue) @@ -143,20 +142,11 @@ return w_yieldvalue if opcode == opcodedesc.END_FINALLY.index: - # unlike CPython, when we reach this opcode the value stack has - # always been set up as follows (topmost first): - # [exception type or None] - # [exception value or None] - # [wrapped stack unroller ] - self.valuestack.pop() # ignore the exception type - self.valuestack.pop() # ignore the exception value - w_unroller = self.valuestack.pop() - unroller = self.space.interpclass_w(w_unroller) + unroller = self.end_finally() if isinstance(unroller, SuspendedUnroller): # go on unrolling the stack block = self.unrollstack(unroller.kind) if block is None: - self.frame_finished_execution = True # for generators return unroller.nomoreblocks() else: next_instr = block.handle(self, unroller) @@ -194,6 +184,8 @@ block = self.blockstack.pop() if (block.handling_mask & unroller_kind) != 0: return block + block.cleanupstack(self) + self.frame_finished_execution = True # for generators return None def unrollstack_and_jump(self, unroller): @@ -504,6 +496,18 @@ block = f.blockstack.pop() block.cleanup(f) # the block knows how to clean up the value stack + def end_finally(f): + # unlike CPython, when we reach this opcode the value stack has + # always been set up as follows (topmost first): + # [exception type or None] + # [exception value or None] + # [wrapped stack unroller ] + f.valuestack.pop() # ignore the exception type + f.valuestack.pop() # ignore the exception value + w_unroller = f.valuestack.pop() + unroller = f.space.interpclass_w(w_unroller) + return unroller + def BUILD_CLASS(f, *ignored): w_methodsdict = f.valuestack.pop() w_bases = f.valuestack.pop() From arigo at codespeak.net Fri Dec 1 12:43:34 2006 From: arigo at codespeak.net (arigo at codespeak.net) Date: Fri, 1 Dec 2006 12:43:34 +0100 (CET) Subject: [pypy-svn] r35196 - in pypy/branch/jit-real-world/pypy: objspace/flow/test rlib Message-ID: <20061201114334.B3E0B1006E@code0.codespeak.net> Author: arigo Date: Fri Dec 1 12:43:32 2006 New Revision: 35196 Modified: pypy/branch/jit-real-world/pypy/objspace/flow/test/test_unroll.py pypy/branch/jit-real-world/pypy/rlib/unroll.py Log: Port of r35194. Modified: pypy/branch/jit-real-world/pypy/objspace/flow/test/test_unroll.py ============================================================================== --- pypy/branch/jit-real-world/pypy/objspace/flow/test/test_unroll.py (original) +++ pypy/branch/jit-real-world/pypy/objspace/flow/test/test_unroll.py Fri Dec 1 12:43:32 2006 @@ -1,3 +1,4 @@ +import operator from pypy.objspace.flow.test.test_objspace import Base from pypy.rlib.unroll import unrolling_zero, unrolling_iterable @@ -38,3 +39,28 @@ graph = self.codetest(f) ops = self.all_operations(graph) assert ops == {'setattr': 3} + + def test_unroll_ifs(self): + operations = unrolling_iterable([operator.lt, + operator.le, + operator.eq, + operator.ne, + operator.gt, + operator.ge]) + def accept(n): + "stub" + def f(x, y): + for op in operations: + if accept(op): + op(x, y) + + graph = self.codetest(f) + ops = self.all_operations(graph) + assert ops == {'simple_call': 6, + 'is_true': 6, + 'lt': 1, + 'le': 1, + 'eq': 1, + 'ne': 1, + 'gt': 1, + 'ge': 1} Modified: pypy/branch/jit-real-world/pypy/rlib/unroll.py ============================================================================== --- pypy/branch/jit-real-world/pypy/rlib/unroll.py (original) +++ pypy/branch/jit-real-world/pypy/rlib/unroll.py Fri Dec 1 12:43:32 2006 @@ -43,12 +43,13 @@ def __init__(self, iterable): self._items = list(iterable) + self._head = _unroller(self._items) def __iter__(self): return iter(self._items) def get_unroller(self): - return _unroller(self._items) + return self._head class _unroller(SpecTag): @@ -56,8 +57,10 @@ def __init__(self, items, i=0): self._items = items self._i = i + self._next = None def step(self): v = self._items[self._i] - next = _unroller(self._items, self._i+1) - return v, next + if self._next is None: + self._next = _unroller(self._items, self._i+1) + return v, self._next From arigo at codespeak.net Fri Dec 1 13:12:46 2006 From: arigo at codespeak.net (arigo at codespeak.net) Date: Fri, 1 Dec 2006 13:12:46 +0100 (CET) Subject: [pypy-svn] r35197 - pypy/branch/jit-real-world/pypy/interpreter Message-ID: <20061201121246.1225C10068@code0.codespeak.net> Author: arigo Date: Fri Dec 1 13:12:44 2006 New Revision: 35197 Modified: pypy/branch/jit-real-world/pypy/interpreter/pyopcode.py Log: (arigo, arre in parallel) Work around bad support for "raise Class, instance" in the flow space. Modified: pypy/branch/jit-real-world/pypy/interpreter/pyopcode.py ============================================================================== --- pypy/branch/jit-real-world/pypy/interpreter/pyopcode.py (original) +++ pypy/branch/jit-real-world/pypy/interpreter/pyopcode.py Fri Dec 1 13:12:44 2006 @@ -91,8 +91,13 @@ block = self.unrollstack(SApplicationException.kind) if block is None: # no handler found for the OperationError - tb = cpython_tb() - raise OperationError, operr, tb + if we_are_translated(): + raise operr + else: + # try to preserve the CPython-level traceback + import sys + tb = sys.exc_info()[2] + raise OperationError, operr, tb else: unroller = SApplicationException(operr) next_instr = block.handle(self, unroller) @@ -868,13 +873,6 @@ ### ____________________________________________________________ ### - -def cpython_tb(): - """NOT_RPYTHON""" - import sys - return sys.exc_info()[2] -cpython_tb._annspecialcase_ = "override:ignore" - class Reraise(Exception): """Signal an application-level OperationError that should not grow a new traceback entry nor trigger the trace hook.""" From arigo at codespeak.net Fri Dec 1 13:22:41 2006 From: arigo at codespeak.net (arigo at codespeak.net) Date: Fri, 1 Dec 2006 13:22:41 +0100 (CET) Subject: [pypy-svn] r35198 - in pypy/branch/jit-real-world/pypy: interpreter objspace/flow Message-ID: <20061201122241.57B3510068@code0.codespeak.net> Author: arigo Date: Fri Dec 1 13:22:38 2006 New Revision: 35198 Modified: pypy/branch/jit-real-world/pypy/interpreter/pyopcode.py pypy/branch/jit-real-world/pypy/objspace/flow/framestate.py Log: Turn state_pack_variables() into a "constructor" static method, instead of using instantiate() in the flow space. This avoids making multiple instances of SBreakLoop (needed for flowgraphing the BREAK_LOOP function itself, which manipulates the singleton directly). Modified: pypy/branch/jit-real-world/pypy/interpreter/pyopcode.py ============================================================================== --- pypy/branch/jit-real-world/pypy/interpreter/pyopcode.py (original) +++ pypy/branch/jit-real-world/pypy/interpreter/pyopcode.py Fri Dec 1 13:22:38 2006 @@ -901,12 +901,10 @@ """ def nomoreblocks(self): raise BytecodeCorruption("misplaced bytecode - should not return") - # for the flow object space, a way to "pickle" and "unpickle" the - # ControlFlowException by enumerating the Variables it contains. - def state_unpack_variables(self, space): - return [] # by default, overridden below - def state_pack_variables(self, space, *values_w): - assert len(values_w) == 0 + + # NB. for the flow object space, the state_(un)pack_variables methods + # give a way to "pickle" and "unpickle" the SuspendedUnroller by + # enumerating the Variables it contains. class SReturnValue(SuspendedUnroller): """Signals a 'return' statement. @@ -916,10 +914,12 @@ self.w_returnvalue = w_returnvalue def nomoreblocks(self): return self.w_returnvalue + def state_unpack_variables(self, space): return [self.w_returnvalue] - def state_pack_variables(self, space, w_returnvalue): - self.w_returnvalue = w_returnvalue + def state_pack_variables(space, w_returnvalue): + return SReturnValue(w_returnvalue) + state_pack_variables = staticmethod(state_pack_variables) class SApplicationException(SuspendedUnroller): """Signals an application-level exception @@ -929,14 +929,23 @@ self.operr = operr def nomoreblocks(self): raise self.operr + def state_unpack_variables(self, space): return [self.operr.w_type, self.operr.w_value] - def state_pack_variables(self, space, w_type, w_value): - self.operr = OperationError(w_type, w_value) + def state_pack_variables(space, w_type, w_value): + return SApplicationException(OperationError(w_type, w_value)) + state_pack_variables = staticmethod(state_pack_variables) class SBreakLoop(SuspendedUnroller): """Signals a 'break' statement.""" kind = 0x04 + + def state_unpack_variables(self, space): + return [] + def state_pack_variables(space): + return SBreakLoop.singleton + state_pack_variables = staticmethod(state_pack_variables) + SBreakLoop.singleton = SBreakLoop() class SContinueLoop(SuspendedUnroller): @@ -945,10 +954,12 @@ kind = 0x08 def __init__(self, jump_to): self.jump_to = jump_to + def state_unpack_variables(self, space): return [space.wrap(self.jump_to)] - def state_pack_variables(self, space, w_jump_to): - self.jump_to = space.int_w(w_jump_to) + def state_pack_variables(space, w_jump_to): + return SContinueLoop(space.int_w(w_jump_to)) + state_pack_variables = staticmethod(state_pack_variables) class FrameBlock: Modified: pypy/branch/jit-real-world/pypy/objspace/flow/framestate.py ============================================================================== --- pypy/branch/jit-real-world/pypy/objspace/flow/framestate.py (original) +++ pypy/branch/jit-real-world/pypy/objspace/flow/framestate.py Fri Dec 1 13:22:38 2006 @@ -1,7 +1,6 @@ from pypy.interpreter.pyframe import PyFrame from pypy.interpreter.pyopcode import SuspendedUnroller from pypy.interpreter.error import OperationError -from pypy.rlib.objectmodel import instantiate from pypy.rlib.unroll import SpecTag from pypy.objspace.flow.model import * @@ -175,6 +174,5 @@ unrollerclass, argcount = UNPICKLE_TAGS[item] arguments = lst[i+1: i+1+argcount] del lst[i+1: i+1+argcount] - unroller = instantiate(unrollerclass) - unroller.state_pack_variables(space, *arguments) + unroller = unrollerclass.state_pack_variables(space, *arguments) lst[i] = space.wrap(unroller) From fijal at codespeak.net Fri Dec 1 16:43:36 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 1 Dec 2006 16:43:36 +0100 (CET) Subject: [pypy-svn] r35203 - pypy/dist/pypy/translator/c/src Message-ID: <20061201154336.3036D10063@code0.codespeak.net> Author: fijal Date: Fri Dec 1 16:43:34 2006 New Revision: 35203 Modified: pypy/dist/pypy/translator/c/src/support.h Log: Seems that symbol has Modified: pypy/dist/pypy/translator/c/src/support.h ============================================================================== --- pypy/dist/pypy/translator/c/src/support.h (original) +++ pypy/dist/pypy/translator/c/src/support.h Fri Dec 1 16:43:34 2006 @@ -45,7 +45,7 @@ # define RPyAssert(x, msg) /* nothing */ #endif -#ifdef __RPyListOfString_New /* :-( */ +#ifdef _RPyListOfString_New /* :-( */ # define HAVE_RPY_LIST_OF_STRING #endif From ericvrp at codespeak.net Fri Dec 1 17:30:15 2006 From: ericvrp at codespeak.net (ericvrp at codespeak.net) Date: Fri, 1 Dec 2006 17:30:15 +0100 (CET) Subject: [pypy-svn] r35204 - in pypy/dist/pypy/jit/codegen/llvm: . lib test Message-ID: <20061201163015.5492E10076@code0.codespeak.net> Author: ericvrp Date: Fri Dec 1 17:30:13 2006 New Revision: 35204 Modified: pypy/dist/pypy/jit/codegen/llvm/lib/libllvmjit.cpp pypy/dist/pypy/jit/codegen/llvm/lib/libllvmjit.h pypy/dist/pypy/jit/codegen/llvm/llvmjit.py pypy/dist/pypy/jit/codegen/llvm/test/test_llvmjit.py Log: llvmjit test_call_global_function is working. Some function renaming to stay closer to the llvm names. Modified: pypy/dist/pypy/jit/codegen/llvm/lib/libllvmjit.cpp ============================================================================== --- pypy/dist/pypy/jit/codegen/llvm/lib/libllvmjit