[pypy-svn] r35184 - in pypy/branch/jit-real-world/pypy: annotation config interpreter interpreter/test module/_pickle_support objspace/flow tool
arigo at codespeak.net
arigo at codespeak.net
Fri Dec 1 03:44:42 CET 2006
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("<i", imp.get_magic())
-NESTED = 1
-GENERATOR = 2
-
-frame_classes = []
-
-def setup_frame_classes():
- "NOT_RPYTHON"
- from pypy.interpreter.pyopcode import PyInterpFrame
- from pypy.interpreter.nestedscope import PyNestedScopeFrame
- from pypy.interpreter.generator import GeneratorFrameMixin
-
- class PyGeneratorFrame(GeneratorFrameMixin, PyInterpFrame):
- pass
-
- class PyNestedScopeGeneratorFrame(GeneratorFrameMixin, PyNestedScopeFrame):
- pass
-
- frame_classes.extend([None]*4)
- frame_classes[0] = PyInterpFrame
- frame_classes[NESTED] = PyNestedScopeFrame
- frame_classes[GENERATOR] = PyGeneratorFrame
- frame_classes[NESTED|GENERATOR] = PyNestedScopeGeneratorFrame
-
class PyCode(eval.Code):
"CPython-style code objects."
@@ -248,21 +225,10 @@
frame.init_cells()
return frame.run()
- def get_frame_class(self):
- # select the appropriate kind of frame
- if not frame_classes:
- setup_frame_classes() # lazily
- choose = 0
- if self.co_cellvars or self.co_freevars:
- choose |= NESTED
- if self.co_flags & CO_GENERATOR:
- choose |= GENERATOR
- Frame = frame_classes[choose]
- return Frame
-
def create_frame(self, space, w_globals, closure=None):
"Create an empty PyFrame suitable for this code object."
- return self.get_frame_class()(space, self, w_globals, closure)
+ from pypy.interpreter import pyframe
+ return pyframe.PyFrame(space, self, w_globals, closure)
def getvarnames(self):
return self.co_varnames
@@ -277,20 +243,6 @@
else:
return None
- def initialize_frame_scopes(self, frame):
- # regular functions always have CO_OPTIMIZED and CO_NEWLOCALS.
- # class bodies only have CO_NEWLOCALS.
- # CO_NEWLOCALS: make a locals dict unless optimized is also set
- # CO_OPTIMIZED: no locals dict needed at all
- flags = self.co_flags
- if flags & CO_OPTIMIZED:
- return
- if flags & CO_NEWLOCALS:
- frame.w_locals = frame.space.newdict()
- else:
- assert frame.w_globals is not None
- frame.w_locals = frame.w_globals
-
def getjoinpoints(self):
"""Compute the bytecode positions that are potential join points
(for FlowObjSpace)"""
Modified: pypy/branch/jit-real-world/pypy/interpreter/pyframe.py
==============================================================================
--- pypy/branch/jit-real-world/pypy/interpreter/pyframe.py (original)
+++ pypy/branch/jit-real-world/pypy/interpreter/pyframe.py Fri Dec 1 03:43:34 2006
@@ -1,11 +1,11 @@
""" PyFrame class implementation with the interpreter main loop.
"""
-from pypy.interpreter import eval, baseobjspace
+from pypy.tool.pairtype import extendabletype
+from pypy.interpreter import eval, baseobjspace, pycode
from pypy.interpreter.miscutils import Stack, FixedStack
from pypy.interpreter.error import OperationError
from pypy.interpreter import pytraceback
-from pypy.rlib.rarithmetic import r_uint, intmask
import opcode
from pypy.rlib.objectmodel import we_are_translated, instantiate
from pypy.rlib import rstack # for resume points
@@ -19,13 +19,7 @@
HAVE_ARGUMENT = opcode.HAVE_ARGUMENT
-def cpython_tb():
- """NOT_RPYTHON"""
- import sys
- return sys.exc_info()[2]
-cpython_tb._annspecialcase_ = "override:ignore"
-
-class PyFrame(eval.EvalFrame):
+class PyFrame(eval.Frame):
"""Represents a frame for a regular Python function
that needs to be interpreted.
@@ -37,9 +31,21 @@
* 'w_locals' is the locals dictionary to use
* 'w_globals' is the attached globals dictionary
* 'builtin' is the attached built-in module
- * 'valuestack', 'blockstack', 'next_instr' control the interpretation
+ * 'valuestack', 'blockstack', control the interpretation
"""
+ __metaclass__ = extendabletype
+
+ frame_finished_execution = False
+ last_instr = -1
+ last_exception = None
+ f_back = None
+ w_f_trace = None
+ # For tracing
+ instr_lb = 0
+ instr_ub = -1
+ instr_prev = -1
+
def __init__(self, space, code, w_globals, closure):
self.pycode = code
eval.Frame.__init__(self, space, w_globals, code.co_nlocals)
@@ -51,37 +57,72 @@
else:
self.valuestack = Stack()
self.blockstack = Stack()
- self.last_exception = None
- self.next_instr = r_uint(0) # Force it unsigned for performance reasons.
self.builtin = space.builtin.pick_builtin(w_globals)
# regular functions always have CO_OPTIMIZED and CO_NEWLOCALS.
# class bodies only have CO_NEWLOCALS.
- code.initialize_frame_scopes(self)
+ self.initialize_frame_scopes(closure)
self.fastlocals_w = [None]*self.numlocals
- self.w_f_trace = None
- self.last_instr = -1
- self.f_back = None
self.f_lineno = self.pycode.co_firstlineno
- # For tracing
- self.instr_lb = 0
- self.instr_ub = -1
- self.instr_prev = -1
+ def initialize_frame_scopes(self, closure):
+ # regular functions always have CO_OPTIMIZED and CO_NEWLOCALS.
+ # class bodies only have CO_NEWLOCALS.
+ # CO_NEWLOCALS: make a locals dict unless optimized is also set
+ # CO_OPTIMIZED: no locals dict needed at all
+ # NB: this method is overridden in nestedscope.py
+ flags = self.pycode.co_flags
+ if flags & pycode.CO_OPTIMIZED:
+ return
+ if flags & pycode.CO_NEWLOCALS:
+ self.w_locals = self.space.newdict()
+ else:
+ assert self.w_globals is not None
+ self.w_locals = self.w_globals
+
+ def run(self):
+ """Start this frame's execution."""
+ if self.pycode.co_flags & pycode.CO_GENERATOR:
+ from pypy.interpreter.generator import GeneratorIterator
+ return self.space.wrap(GeneratorIterator(self))
+ else:
+ return self.execute_frame()
+
+ def execute_frame(self):
+ """Execute this frame. Main entry point to the interpreter."""
+ executioncontext = self.space.getexecutioncontext()
+ executioncontext.enter(self)
+ try:
+ executioncontext.call_trace(self)
+ code = self.pycode.co_code
+ # Execution starts just after the last_instr. Initially,
+ # last_instr is -1. After a generator suspends it points to
+ # the YIELD_VALUE instruction.
+ next_instr = self.last_instr + 1
+ w_exitvalue = self.dispatch(code, next_instr, executioncontext)
+ executioncontext.return_trace(self, w_exitvalue)
+ # on exit, we try to release self.last_exception -- breaks an
+ # obvious reference cycle, so it helps refcounting implementations
+ self.last_exception = None
+ finally:
+ executioncontext.leave(self)
+ return w_exitvalue
+ execute_frame.insert_stack_check_here = True
+
def descr__reduce__(self, space):
from pypy.interpreter.mixedmodule import MixedModule
from pypy.module._pickle_support import maker # helper fns
- from pypy.interpreter.nestedscope import PyNestedScopeFrame
w_mod = space.getbuiltinmodule('_pickle_support')
mod = space.interp_w(MixedModule, w_mod)
new_inst = mod.get('frame_new')
w = space.wrap
nt = space.newtuple
- if isinstance(self, PyNestedScopeFrame):
- w_cells = space.newlist([w(cell) for cell in self.cells])
- else:
+ cells = self._getcells()
+ if cells is None:
w_cells = space.w_None
+ else:
+ w_cells = space.newlist([space.wrap(cell) for cell in cells])
if self.w_f_trace is None:
f_lineno = self.get_last_lineno()
@@ -114,7 +155,7 @@
w_tb, #
self.w_globals,
w(self.last_instr),
- w(self.next_instr),
+ w(self.frame_finished_execution),
w(f_lineno),
w_fastlocals,
space.w_None, #XXX placeholder for f_locals
@@ -134,20 +175,28 @@
from pypy.module._pickle_support import maker # helper fns
from pypy.interpreter.pycode import PyCode
from pypy.interpreter.module import Module
- from pypy.interpreter.nestedscope import PyNestedScopeFrame, Cell
args_w = space.unpackiterable(w_args)
w_f_back, w_builtin, w_pycode, w_valuestack, w_blockstack, w_exc_value, w_tb,\
- w_globals, w_last_instr, w_next_instr, w_f_lineno, w_fastlocals, w_f_locals, \
+ w_globals, w_last_instr, w_finished, w_f_lineno, w_fastlocals, w_f_locals, \
w_f_trace, w_instr_lb, w_instr_ub, w_instr_prev, w_cells = args_w
- #new_frame = PyFrame(space, pycode, w(globals), None)
- # let the code object create the right kind of frame
- # the distinction is a little over-done but computable
new_frame = self
pycode = space.interp_w(PyCode, w_pycode)
+
+ if space.is_w(w_cells, space.w_None):
+ closure = None
+ cellvars = []
+ else:
+ from pypy.interpreter.nestedscope import Cell
+ cells_w = space.unpackiterable(w_cells)
+ cells = [space.interp_w(Cell, w_cell) for w_cell in cells_w]
+ ncellvars = len(pycode.co_cellvars)
+ cellvars = cells[:ncellvars]
+ closure = cells[ncellvars:]
+
# do not use the instance's __init__ but the base's, because we set
# everything like cells from here
- PyFrame.__init__(self, space, pycode, w_globals, None)
+ PyFrame.__init__(self, space, pycode, w_globals, closure)
new_frame.f_back = space.interp_w(PyFrame, w_f_back, can_be_None=True)
new_frame.builtin = space.interp_w(Module, w_builtin)
new_frame.blockstack.items = [unpickle_block(space, w_blk)
@@ -165,7 +214,7 @@
w_exc_value, tb
)
new_frame.last_instr = space.int_w(w_last_instr)
- new_frame.next_instr = space.int_w(w_next_instr)
+ new_frame.frame_finished_execution = space.is_true(w_finished)
new_frame.f_lineno = space.int_w(w_f_lineno)
new_frame.fastlocals_w = maker.slp_from_tuple_with_nulls(space, w_fastlocals)
@@ -178,9 +227,7 @@
new_frame.instr_ub = space.int_w(w_instr_ub)
new_frame.instr_prev = space.int_w(w_instr_prev)
- if isinstance(self, PyNestedScopeFrame):
- cells_w = space.unpackiterable(w_cells)
- self.cells = [space.interp_w(Cell, w_cell) for w_cell in cells_w]
+ self._setcellvars(cellvars)
def hide(self):
return self.pycode.hidden_applevel
@@ -203,67 +250,18 @@
def init_cells(self):
"""Initialize cellvars from self.fastlocals_w
- This is overridden in PyNestedScopeFrame"""
+ This is overridden in nestedscope.py"""
pass
def getclosure(self):
return None
- def eval(self, executioncontext):
- "Interpreter main loop!"
- try:
- executioncontext.call_trace(self)
- self.last_instr = 0
- while True:
- try:
- try:
- try:
- if we_are_translated():
- # always raising, put the resume point just before!
- rstack.resume_point("eval", self, executioncontext)
- code = self.pycode.co_code
- self.dispatch_translated(code,
- executioncontext)
- else:
- self.dispatch(executioncontext)
- # catch asynchronous exceptions and turn them
- # into OperationErrors
- except KeyboardInterrupt:
- tb = cpython_tb()
- raise OperationError, OperationError(self.space.w_KeyboardInterrupt,
- self.space.w_None), tb
- except MemoryError:
- tb = cpython_tb()
- raise OperationError, OperationError(self.space.w_MemoryError,
- self.space.w_None), tb
- except RuntimeError, e:
- tb = cpython_tb()
- raise OperationError, OperationError(self.space.w_RuntimeError,
- self.space.wrap("internal error: " + str(e))), tb
-
- except OperationError, e:
- pytraceback.record_application_traceback(
- self.space, e, self, self.last_instr)
- executioncontext.exception_trace(self, e)
- # convert an OperationError into a control flow
- # exception
- raise SApplicationException(e)
-
- except ControlFlowException, ctlflowexc:
- # we have a reason to change the control flow
- # (typically unroll the stack)
- ctlflowexc.action(self)
-
- except ExitFrame, e:
- # leave that frame
- w_exitvalue = e.w_exitvalue
- executioncontext.return_trace(self, w_exitvalue)
- # on exit, we try to release self.last_exception -- breaks an
- # obvious reference cycle, so it helps refcounting implementations
- self.last_exception = None
- return w_exitvalue
- eval.insert_stack_check_here = True
-
+ def _getcells(self):
+ return None
+
+ def _setcellvars(self, cellvars):
+ pass
+
### line numbers ###
# for f*_f_* unwrapping through unwrap_spec in typedef.py
@@ -401,11 +399,7 @@
def get_last_lineno(self):
"Returns the line number of the instruction currently being executed."
- return pytraceback.offset2lineno(self.pycode, intmask(self.next_instr)-1)
-
- def get_next_lineno(self):
- "Returns the line number of the next instruction to execute."
- return pytraceback.offset2lineno(self.pycode, intmask(self.next_instr))
+ return pytraceback.offset2lineno(self.pycode, self.last_instr)
def fget_f_builtins(space, self):
return self.builtin.getdict()
@@ -459,251 +453,11 @@
def fget_f_restricted(space, self):
return space.wrap(self.builtin is not space.builtin)
-### Frame Blocks ###
-
-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)
-
- def unroll(self, frame, unroller):
- "Clean up a frame when we abnormally exit the block."
- self.cleanupstack(frame)
- return False # continue to unroll
-
- # 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'
-
- def unroll(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)
- frame.next_instr = unroller.jump_to
- return True # stop unrolling
- self.cleanupstack(frame)
- if isinstance(unroller, SBreakLoop):
- # jump to the end of the loop
- frame.next_instr = self.handlerposition
- return True # stop unrolling
- return False
-
-
-class ExceptBlock(FrameBlock):
- """An try:except: block. Stores the position of the exception handler."""
-
- _opname = 'SETUP_EXCEPT'
-
- def unroll(self, frame, unroller):
- self.cleanupstack(frame)
- if isinstance(unroller, SApplicationException):
- # push the exception to the value stack for inspection by the
- # exception handler (the code after the except:)
- 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(unroller.wrap(frame.space))
- frame.valuestack.push(operationerr.w_value)
- frame.valuestack.push(operationerr.w_type)
- frame.next_instr = self.handlerposition # jump to the handler
- return True # stop unrolling
- return False
-
-
-class FinallyBlock(FrameBlock):
- """A try:finally: block. Stores the position of the exception handler."""
-
- _opname = 'SETUP_FINALLY'
-
- 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 unroll(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(unroller.wrap(frame.space))
- frame.valuestack.push(frame.space.w_None)
- frame.valuestack.push(frame.space.w_None)
- frame.next_instr = self.handlerposition # jump to the handler
- return True # stop unrolling
-
-
-### Internal exceptions that change the control flow ###
-### and (typically) unroll the block stack ###
-
-class ControlFlowException(Exception):
- """Abstract base class for interpreter-level exceptions 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, we don't think this is needed
- WHY_RETURN, SReturnValue
- WHY_BREAK, SBreakLoop
- WHY_CONTINUE, SContinueLoop
- WHY_YIELD SYieldValue
-
- """
- def action(self, frame):
- "Default unroller implementation."
- while not frame.blockstack.empty():
- block = frame.blockstack.pop()
- if block.unroll(frame, self):
- break
- else:
- self.emptystack(frame)
-
- def emptystack(self, frame):
- "Default behavior when the block stack is exhausted."
- # could occur e.g. when a BREAK_LOOP is not actually within a loop
- raise BytecodeCorruption, "block stack exhausted"
-
- def wrap(self, space):
- return space.wrap(SuspendedUnroller(self))
-
- # 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 SuspendedUnroller(baseobjspace.Wrappable):
- """A wrappable box around a ControlFlowException."""
- def __init__(self, flowexc):
- self.flowexc = flowexc
-
-class SApplicationException(ControlFlowException):
- """Unroll the stack because of an application-level exception
- (i.e. an OperationException)."""
-
- def __init__(self, operr):
- self.operr = operr
-
- def action(self, frame):
- frame.last_exception = self.operr
- ControlFlowException.action(self, frame)
-
- def emptystack(self, frame):
- # propagate the exception to the caller
- from pypy.rlib.objectmodel import we_are_translated
- if we_are_translated():
- raise self.operr
- else:
- # try to preserve the interp-level traceback
- if self.operr.debug_excs:
- _, _, tb = self.operr.debug_excs[-1]
- else:
- tb = None
- raise OperationError, self.operr, tb
-
- 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(ControlFlowException):
- """Signals a 'break' statement."""
-
-class SContinueLoop(ControlFlowException):
- """Signals a 'continue' statement.
- Argument is the bytecode position of the beginning of the loop."""
-
- 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 SReturnValue(ControlFlowException):
- """Signals a 'return' statement.
- Argument is the wrapped object to return."""
-
- def __init__(self, w_returnvalue):
- self.w_returnvalue = w_returnvalue
-
- def emptystack(self, frame):
- raise ExitFrame(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 ExitFrame(Exception):
- """Signals the end of the frame execution.
- The argument is the returned or yielded value, already wrapped."""
- def __init__(self, w_exitvalue):
- self.w_exitvalue = w_exitvalue
-
-class BytecodeCorruption(ValueError):
- """Detected bytecode corruption. Never caught; it's an error."""
-
# ____________________________________________________________
-def setup_block_classes():
- "NOT_RPYTHON"
- import types
- for cls in globals().values():
- if isinstance(cls, (types.ClassType,type)):
- if issubclass(cls, FrameBlock) and hasattr(cls, '_opname'):
- block_classes[cls._opname] = cls
-block_classes = {}
-setup_block_classes()
-
def get_block_class(opname):
# select the appropriate kind of block
+ from pypy.interpreter.pyopcode import block_classes
return block_classes[opname]
def unpickle_block(space, w_tup):
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 03:43:34 2006
@@ -5,22 +5,22 @@
"""
from pypy.interpreter.error import OperationError
-from pypy.interpreter.baseobjspace import UnpackValueError
+from pypy.interpreter.baseobjspace import UnpackValueError, Wrappable
from pypy.interpreter import gateway, function, eval
from pypy.interpreter import pyframe, pytraceback
-from pypy.interpreter.miscutils import InitializedClass
from pypy.interpreter.argument import Arguments, ArgumentsFromValuestack
from pypy.interpreter.pycode import PyCode
-from pypy.interpreter.opcodeorder import opcodeorder
from pypy.tool.sourcetools import func_with_new_name
from pypy.rlib.objectmodel import we_are_translated, hint
-from pypy.rlib.rarithmetic import intmask
-from pypy.tool import stdlib_opcode as pythonopcode
+from pypy.rlib.rarithmetic import r_uint, intmask
+from pypy.tool.stdlib_opcode import opcodedesc, HAVE_ARGUMENT
+from pypy.tool.stdlib_opcode import unrolling_opcode_descs
+from pypy.tool.stdlib_opcode import opcode_method_names
from pypy.rlib import rstack # for resume points
def unaryoperation(operationname):
"""NOT_RPYTHON"""
- def opimpl(f):
+ def opimpl(f, *ignored):
operation = getattr(f.space, operationname)
w_1 = f.valuestack.pop()
w_result = operation(w_1)
@@ -30,7 +30,7 @@
def binaryoperation(operationname):
"""NOT_RPYTHON"""
- def opimpl(f):
+ def opimpl(f, *ignored):
operation = getattr(f.space, operationname)
w_2 = f.valuestack.pop()
w_1 = f.valuestack.pop()
@@ -40,52 +40,167 @@
return func_with_new_name(opimpl, "opcode_impl_for_%s" % operationname)
-class PyInterpFrame(pyframe.PyFrame):
+class __extend__(pyframe.PyFrame):
"""A PyFrame that knows about interpretation of standard Python opcodes
minus the ones related to nested scopes."""
### opcode dispatch ###
-
- # '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
- # Currently, they are always setup in pyopcode.py
- # but it could be a custom table.
- def dispatch(self, ec):
+ def dispatch(self, co_code, next_instr, ec):
while True:
- self.last_instr = intmask(self.next_instr)
+ try:
+ return self.dispatch_bytecode(co_code, next_instr, ec)
+ except OperationError, operr:
+ next_instr = self.handle_operation_error(ec, operr)
+ except Reraise:
+ operr = self.last_exception
+ next_instr = self.handle_operation_error(ec, operr,
+ attach_tb=False)
+ except KeyboardInterrupt:
+ next_instr = self.handle_asynchronous_error(ec,
+ self.space.w_KeyboardInterrupt)
+ except MemoryError:
+ next_instr = self.handle_asynchronous_error(ec,
+ self.space.w_MemoryError)
+ except RuntimeError:
+ if we_are_translated():
+ # stack overflows should be the only kind of RuntimeErrors
+ # in translated PyPy
+ msg = "internal error (stack overflow?)"
+ else:
+ msg = str(e)
+ next_instr = self.handle_asynchronous_error(ec,
+ self.space.w_RuntimeError,
+ self.space.wrap(msg))
+
+ def handle_asynchronous_error(self, ec, w_type, w_value=None):
+ # catch asynchronous exceptions and turn them
+ # into OperationErrors
+ if w_value is None:
+ w_value = self.space.w_None
+ operr = OperationError(w_type, w_value)
+ return self.handle_operation_error(ec, operr)
+
+ def handle_operation_error(self, ec, operr, attach_tb=True):
+ self.last_exception = operr
+ if attach_tb:
+ pytraceback.record_application_traceback(
+ self.space, operr, self, self.last_instr)
+ ec.exception_trace(self, operr)
+
+ block = self.unrollstack(SApplicationException.kind)
+ if block is None:
+ # no handler found for the OperationError
+ tb = cpython_tb()
+ raise OperationError, operr, tb
+ else:
+ unroller = SApplicationException(operr)
+ next_instr = block.handle(self, unroller)
+ return next_instr
+
+ def dispatch_bytecode(self, co_code, next_instr, ec):
+ space = self.space
+ while True:
+ self.last_instr = intmask(next_instr)
ec.bytecode_trace(self)
- self.next_instr = self.last_instr
- opcode = self.nextop()
- if self.space.config.objspace.logbytecodes:
- self.space.bytecodecounts[opcode] = self.space.bytecodecounts.get(opcode, 0) + 1
- if opcode >= 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
More information about the pypy-svn
mailing list