Author: Alex Gaynor <[email protected]>
Branch:
Changeset: r67629:ef88c5a4a263
Date: 2013-10-26 10:32 -0700
http://bitbucket.org/pypy/pypy/changeset/ef88c5a4a263/
Log: Merged nestedscope's frame manipulation code into the proper classes
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -326,9 +326,6 @@
self.builtin_modules = {}
self.reloading_modules = {}
- # import extra modules for side-effects
- import pypy.interpreter.nestedscope # register *_DEREF bytecodes
-
self.interned_strings = {}
self.actionflag = ActionFlag() # changed by the signal module
self.check_signal_action = None # changed by the signal module
diff --git a/pypy/interpreter/nestedscope.py b/pypy/interpreter/nestedscope.py
--- a/pypy/interpreter/nestedscope.py
+++ b/pypy/interpreter/nestedscope.py
@@ -1,8 +1,5 @@
-from rpython.rlib import jit
from rpython.tool.uid import uid
-from pypy.interpreter import function, pycode, pyframe
-from pypy.interpreter.astcompiler import consts
from pypy.interpreter.baseobjspace import W_Root
from pypy.interpreter.error import OperationError
from pypy.interpreter.mixedmodule import MixedModule
@@ -73,151 +70,3 @@
return self.get()
except ValueError:
raise OperationError(space.w_ValueError, space.wrap("Cell is
empty"))
-
-
-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."""
-
- # Cell Vars:
- # my local variables that are exposed to my inner functions
- # Free Vars:
- # variables coming from a parent function in which i'm nested
- # 'closure' is a list of Cell instances: the received free vars.
-
- @jit.unroll_safe
- def initialize_frame_scopes(self, outer_func, code):
- super_initialize_frame_scopes(self, outer_func, code)
- ncellvars = len(code.co_cellvars)
- nfreevars = len(code.co_freevars)
- if not nfreevars:
- if not ncellvars:
- self.cells = []
- return # no self.cells needed - fast path
- elif outer_func is None:
- space = self.space
- raise OperationError(space.w_TypeError,
- space.wrap("directly executed code object "
- "may not contain free variables"))
- if outer_func and outer_func.closure:
- closure_size = len(outer_func.closure)
- else:
- closure_size = 0
- if closure_size != nfreevars:
- raise ValueError("code object received a closure with "
- "an unexpected number of free variables")
- self.cells = [None] * (ncellvars + nfreevars)
- for i in range(ncellvars):
- self.cells[i] = Cell()
- for i in range(nfreevars):
- self.cells[i + ncellvars] = outer_func.closure[i]
-
- 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"))
- self.cells[:ncellvars] = cellvars
-
- @jit.unroll_safe
- def fast2locals(self):
- super_fast2locals(self)
- # cellvars are values exported to inner scopes
- # freevars are values coming from outer scopes
- freevarnames = list(self.pycode.co_cellvars)
- if self.pycode.co_flags & consts.CO_OPTIMIZED:
- freevarnames.extend(self.pycode.co_freevars)
- for i in range(len(freevarnames)):
- name = freevarnames[i]
- cell = self.cells[i]
- try:
- w_value = cell.get()
- except ValueError:
- pass
- else:
- w_name = self.space.wrap(name)
- self.space.setitem(self.w_locals, w_name, w_value)
-
- @jit.unroll_safe
- def locals2fast(self):
- super_locals2fast(self)
- freevarnames = self.pycode.co_cellvars + self.pycode.co_freevars
- for i in range(len(freevarnames)):
- name = freevarnames[i]
- cell = self.cells[i]
- w_name = self.space.wrap(name)
- try:
- w_value = self.space.getitem(self.w_locals, w_name)
- except OperationError, e:
- if not e.match(self.space, self.space.w_KeyError):
- raise
- else:
- cell.set(w_value)
-
- @jit.unroll_safe
- def init_cells(self):
- args_to_copy = self.pycode._args_as_cellvars
- for i in range(len(args_to_copy)):
- argnum = args_to_copy[i]
- if argnum >= 0:
- self.cells[i].set(self.locals_stack_w[argnum])
-
- def getfreevarname(self, index):
- freevarnames = self.pycode.co_cellvars + self.pycode.co_freevars
- return freevarnames[index]
-
- def iscellvar(self, index):
- # is the variable given by index a cell or a free var?
- return index < len(self.pycode.co_cellvars)
-
- ### extra opcodes ###
-
- def LOAD_CLOSURE(self, varindex, next_instr):
- # nested scopes: access the cell object
- cell = self.cells[varindex]
- w_value = self.space.wrap(cell)
- self.pushvalue(w_value)
-
- def LOAD_DEREF(self, varindex, next_instr):
- # nested scopes: access a variable through its cell object
- cell = self.cells[varindex]
- try:
- w_value = cell.get()
- except ValueError:
- varname = self.getfreevarname(varindex)
- if self.iscellvar(varindex):
- message = "local variable '%s' referenced before assignment" %
varname
- w_exc_type = self.space.w_UnboundLocalError
- else:
- message = ("free variable '%s' referenced before assignment"
- " in enclosing scope" % varname)
- w_exc_type = self.space.w_NameError
- raise OperationError(w_exc_type, self.space.wrap(message))
- else:
- self.pushvalue(w_value)
-
- def STORE_DEREF(self, varindex, next_instr):
- # nested scopes: access a variable through its cell object
- w_newvalue = self.popvalue()
- cell = self.cells[varindex]
- cell.set(w_newvalue)
-
- @jit.unroll_safe
- def MAKE_CLOSURE(self, numdefaults, next_instr):
- w_codeobj = self.popvalue()
- codeobj = self.space.interp_w(pycode.PyCode, w_codeobj)
- w_freevarstuple = self.popvalue()
- freevars = [self.space.interp_w(Cell, cell)
- for cell in self.space.fixedview(w_freevarstuple)]
- defaultarguments = self.popvalues(numdefaults)
- fn = function.Function(self.space, codeobj, self.w_globals,
- defaultarguments, freevars)
- self.pushvalue(self.space.wrap(fn))
diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py
--- a/pypy/interpreter/pyframe.py
+++ b/pypy/interpreter/pyframe.py
@@ -10,9 +10,11 @@
from pypy.interpreter import pycode, pytraceback
from pypy.interpreter.argument import Arguments
+from pypy.interpreter.astcompiler import consts
from pypy.interpreter.baseobjspace import W_Root
from pypy.interpreter.error import OperationError, operationerrfmt
from pypy.interpreter.executioncontext import ExecutionContext
+from pypy.interpreter.nestedscope import Cell
from pypy.tool import stdlib_opcode
# Define some opcodes used
@@ -26,8 +28,6 @@
"""Represents a frame for a regular Python function
that needs to be interpreted.
- See also pyopcode.PyStandardFrame and nestedscope.PyNestedScopeFrame.
-
Public fields:
* 'space' is the object space this frame is running in
* 'code' is the PyCode object this frame runs
@@ -35,6 +35,12 @@
* 'w_globals' is the attached globals dictionary
* 'builtin' is the attached built-in module
* 'valuestack_w', 'blockstack', control the interpretation
+
+ Cell Vars:
+ my local variables that are exposed to my inner functions
+ Free Vars:
+ variables coming from a parent function in which i'm nested
+ 'closure' is a list of Cell instances: the received free vars.
"""
__metaclass__ = extendabletype
@@ -119,6 +125,7 @@
else:
return self.space.builtin
+ @jit.unroll_safe
def initialize_frame_scopes(self, outer_func, code):
# regular functions always have CO_OPTIMIZED and CO_NEWLOCALS.
# class bodies only have CO_NEWLOCALS.
@@ -126,13 +133,36 @@
# CO_OPTIMIZED: no locals dict needed at all
# NB: this method is overridden in nestedscope.py
flags = code.co_flags
- if flags & pycode.CO_OPTIMIZED:
- return
- if flags & pycode.CO_NEWLOCALS:
- self.w_locals = self.space.newdict(module=True)
+ if not (flags & pycode.CO_OPTIMIZED):
+ if flags & pycode.CO_NEWLOCALS:
+ self.w_locals = self.space.newdict(module=True)
+ else:
+ assert self.w_globals is not None
+ self.w_locals = self.w_globals
+
+ ncellvars = len(code.co_cellvars)
+ nfreevars = len(code.co_freevars)
+ if not nfreevars:
+ if not ncellvars:
+ self.cells = []
+ return # no self.cells needed - fast path
+ elif outer_func is None:
+ space = self.space
+ raise OperationError(space.w_TypeError,
+ space.wrap("directly executed code object "
+ "may not contain free variables"))
+ if outer_func and outer_func.closure:
+ closure_size = len(outer_func.closure)
else:
- assert self.w_globals is not None
- self.w_locals = self.w_globals
+ closure_size = 0
+ if closure_size != nfreevars:
+ raise ValueError("code object received a closure with "
+ "an unexpected number of free variables")
+ self.cells = [None] * (ncellvars + nfreevars)
+ for i in range(ncellvars):
+ self.cells[i] = Cell()
+ for i in range(nfreevars):
+ self.cells[i + ncellvars] = outer_func.closure[i]
def run(self):
"""Start this frame's execution."""
@@ -320,7 +350,7 @@
w = space.wrap
nt = space.newtuple
- cells = self._getcells()
+ cells = self.cells
if cells is None:
w_cells = space.w_None
else:
@@ -489,6 +519,23 @@
if not e.match(self.space, self.space.w_KeyError):
raise
+ # cellvars are values exported to inner scopes
+ # freevars are values coming from outer scopes
+ freevarnames = list(self.pycode.co_cellvars)
+ if self.pycode.co_flags & consts.CO_OPTIMIZED:
+ freevarnames.extend(self.pycode.co_freevars)
+ for i in range(len(freevarnames)):
+ name = freevarnames[i]
+ cell = self.cells[i]
+ try:
+ w_value = cell.get()
+ except ValueError:
+ pass
+ else:
+ w_name = self.space.wrap(name)
+ self.space.setitem(self.w_locals, w_name, w_value)
+
+
@jit.unroll_safe
def locals2fast(self):
# Copy values from self.w_locals to the fastlocals
@@ -510,19 +557,39 @@
self.setfastscope(new_fastlocals_w)
+ freevarnames = self.pycode.co_cellvars + self.pycode.co_freevars
+ for i in range(len(freevarnames)):
+ name = freevarnames[i]
+ cell = self.cells[i]
+ w_name = self.space.wrap(name)
+ try:
+ w_value = self.space.getitem(self.w_locals, w_name)
+ except OperationError, e:
+ if not e.match(self.space, self.space.w_KeyError):
+ raise
+ else:
+ cell.set(w_value)
+
+ @jit.unroll_safe
def init_cells(self):
- """Initialize cellvars from self.locals_stack_w.
- This is overridden in nestedscope.py"""
- pass
+ """
+ Initialize cellvars from self.locals_stack_w.
+ """
+ args_to_copy = self.pycode._args_as_cellvars
+ for i in range(len(args_to_copy)):
+ argnum = args_to_copy[i]
+ if argnum >= 0:
+ self.cells[i].set(self.locals_stack_w[argnum])
def getclosure(self):
return None
- def _getcells(self):
- return None
-
def _setcellvars(self, cellvars):
- pass
+ ncellvars = len(self.pycode.co_cellvars)
+ if len(cellvars) != ncellvars:
+ raise OperationError(self.space.w_TypeError,
+ self.space.wrap("bad cellvars"))
+ self.cells[:ncellvars] = cellvars
def fget_code(self, space):
return space.wrap(self.getcode())
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -4,16 +4,19 @@
The rest, dealing with variables in optimized ways, is in nestedscope.py.
"""
-import sys
-from pypy.interpreter.error import OperationError, operationerrfmt
-from pypy.interpreter.baseobjspace import W_Root
-from pypy.interpreter import gateway, function, eval, pyframe, pytraceback
-from pypy.interpreter.pycode import PyCode, BytecodeCorruption
-from rpython.tool.sourcetools import func_with_new_name
+from rpython.rlib import jit, rstackovf
from rpython.rlib.objectmodel import we_are_translated
-from rpython.rlib import jit, rstackovf
from rpython.rlib.rarithmetic import r_uint, intmask
from rpython.rlib.debug import check_nonneg
+from rpython.tool.sourcetools import func_with_new_name
+
+from pypy.interpreter import (
+ gateway, function, eval, pyframe, pytraceback, pycode
+)
+from pypy.interpreter.baseobjspace import W_Root
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.interpreter.nestedscope import Cell
+from pypy.interpreter.pycode import PyCode, BytecodeCorruption
from pypy.tool.stdlib_opcode import bytecode_spec
def unaryoperation(operationname):
@@ -501,6 +504,44 @@
assert w_newvalue is not None
self.locals_stack_w[varindex] = w_newvalue
+ def getfreevarname(self, index):
+ freevarnames = self.pycode.co_cellvars + self.pycode.co_freevars
+ return freevarnames[index]
+
+ def iscellvar(self, index):
+ # is the variable given by index a cell or a free var?
+ return index < len(self.pycode.co_cellvars)
+
+ def LOAD_DEREF(self, varindex, next_instr):
+ # nested scopes: access a variable through its cell object
+ cell = self.cells[varindex]
+ try:
+ w_value = cell.get()
+ except ValueError:
+ varname = self.getfreevarname(varindex)
+ if self.iscellvar(varindex):
+ message = "local variable '%s' referenced before assignment" %
varname
+ w_exc_type = self.space.w_UnboundLocalError
+ else:
+ message = ("free variable '%s' referenced before assignment"
+ " in enclosing scope" % varname)
+ w_exc_type = self.space.w_NameError
+ raise OperationError(w_exc_type, self.space.wrap(message))
+ else:
+ self.pushvalue(w_value)
+
+ def STORE_DEREF(self, varindex, next_instr):
+ # nested scopes: access a variable through its cell object
+ w_newvalue = self.popvalue()
+ cell = self.cells[varindex]
+ cell.set(w_newvalue)
+
+ def LOAD_CLOSURE(self, varindex, next_instr):
+ # nested scopes: access the cell object
+ cell = self.cells[varindex]
+ w_value = self.space.wrap(cell)
+ self.pushvalue(w_value)
+
def POP_TOP(self, oparg, next_instr):
self.popvalue()
@@ -1185,6 +1226,18 @@
defaultarguments)
self.pushvalue(self.space.wrap(fn))
+ @jit.unroll_safe
+ def MAKE_CLOSURE(self, numdefaults, next_instr):
+ w_codeobj = self.popvalue()
+ codeobj = self.space.interp_w(pycode.PyCode, w_codeobj)
+ w_freevarstuple = self.popvalue()
+ freevars = [self.space.interp_w(Cell, cell)
+ for cell in self.space.fixedview(w_freevarstuple)]
+ defaultarguments = self.popvalues(numdefaults)
+ fn = function.Function(self.space, codeobj, self.w_globals,
+ defaultarguments, freevars)
+ self.pushvalue(self.space.wrap(fn))
+
def BUILD_SLICE(self, numargs, next_instr):
if numargs == 3:
w_step = self.popvalue()
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit