Author: arigo <[email protected]>
Branch:
Changeset: r57021:97e1d2c15876
Date: 2012-08-30 18:37 +0200
http://bitbucket.org/pypy/pypy/changeset/97e1d2c15876/
Log: Merged in rlamy/pypy/translation-cleanup (pull request #83)
diff --git a/pypy/annotation/policy.py b/pypy/annotation/policy.py
--- a/pypy/annotation/policy.py
+++ b/pypy/annotation/policy.py
@@ -27,11 +27,6 @@
callback()
del annotator.bookkeeper.pending_specializations[:]
- def _adjust_space_config(self, space):
- # allow to override space options.
- if getattr(self, 'do_imports_immediately', None) is not None:
- space.do_imports_immediately = self.do_imports_immediately
-
class AnnotatorPolicy(BasicAnnotatorPolicy):
"""
Possibly subclass and pass an instance to the annotator to control special
casing during annotation
@@ -67,7 +62,7 @@
def specialize_with_parms(funcdesc, args_s):
return specializer(funcdesc, args_s, *parms)
return specialize_with_parms
-
+
# common specializations
default_specialize = staticmethod(default)
diff --git a/pypy/interpreter/pycode.py b/pypy/interpreter/pycode.py
--- a/pypy/interpreter/pycode.py
+++ b/pypy/interpreter/pycode.py
@@ -19,6 +19,10 @@
from pypy.rlib.objectmodel import compute_hash
from pypy.tool.stdlib_opcode import opcodedesc, HAVE_ARGUMENT
+
+class BytecodeCorruption(Exception):
+ """Detected bytecode corruption. Never caught; it's an error."""
+
# helper
def unpack_str_tuple(space,w_str_tuple):
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -8,7 +8,7 @@
from pypy.interpreter.error import OperationError, operationerrfmt
from pypy.interpreter.baseobjspace import Wrappable
from pypy.interpreter import gateway, function, eval, pyframe, pytraceback
-from pypy.interpreter.pycode import PyCode
+from pypy.interpreter.pycode import PyCode, BytecodeCorruption
from pypy.tool.sourcetools import func_with_new_name
from pypy.rlib.objectmodel import we_are_translated
from pypy.rlib import jit, rstackovf
@@ -1172,9 +1172,6 @@
def __init__(self, operr):
self.operr = operr
-class BytecodeCorruption(Exception):
- """Detected bytecode corruption. Never caught; it's an error."""
-
### Frame Blocks ###
diff --git a/pypy/objspace/flow/bytecode.py b/pypy/objspace/flow/bytecode.py
new file mode 100644
--- /dev/null
+++ b/pypy/objspace/flow/bytecode.py
@@ -0,0 +1,47 @@
+"""
+Bytecode handling classes and functions for use by the flow space.
+"""
+from pypy.interpreter.pycode import PyCode, BytecodeCorruption
+from pypy.tool.stdlib_opcode import (host_bytecode_spec, EXTENDED_ARG,
+ HAVE_ARGUMENT)
+from pypy.interpreter.astcompiler.consts import CO_GENERATOR
+
+class HostCode(PyCode):
+ """
+ A wrapper around a native code object of the host interpreter
+ """
+ opnames = host_bytecode_spec.method_names
+
+ def read(self, pos):
+ """
+ Decode the instruction starting at position ``next_instr``.
+
+ Returns (next_instr, opname, oparg).
+ """
+ co_code = self.co_code
+ opcode = ord(co_code[pos])
+ next_instr = pos + 1
+
+ if opcode >= HAVE_ARGUMENT:
+ lo = ord(co_code[next_instr])
+ hi = ord(co_code[next_instr+1])
+ next_instr += 2
+ oparg = (hi * 256) | lo
+ else:
+ oparg = 0
+
+ while opcode == EXTENDED_ARG:
+ 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 * 65536) | (hi * 256) | lo
+
+ opname = self.opnames[opcode]
+ return next_instr, opname, oparg
+
+ @property
+ def is_generator(self):
+ return bool(self.co_flags & CO_GENERATOR)
diff --git a/pypy/objspace/flow/flowcontext.py
b/pypy/objspace/flow/flowcontext.py
--- a/pypy/objspace/flow/flowcontext.py
+++ b/pypy/objspace/flow/flowcontext.py
@@ -1,16 +1,22 @@
import collections
import sys
+from pypy.tool.error import FlowingError
from pypy.interpreter.executioncontext import ExecutionContext
from pypy.interpreter.error import OperationError
-from pypy.interpreter import pyframe, nestedscope
+from pypy.interpreter.pytraceback import PyTraceback
+from pypy.interpreter import pyframe
+from pypy.interpreter.nestedscope import Cell
+from pypy.interpreter.pycode import CO_OPTIMIZED, CO_NEWLOCALS
from pypy.interpreter.argument import ArgumentsForTranslation
-from pypy.interpreter.astcompiler.consts import CO_GENERATOR
-from pypy.interpreter.pycode import PyCode, cpython_code_signature
-from pypy.objspace.flow import operation
+from pypy.interpreter.pyopcode import (Return, Yield, SuspendedUnroller,
+ SReturnValue, SApplicationException, BytecodeCorruption, Reraise,
+ RaiseWithExplicitTraceback)
+from pypy.objspace.flow.operation import (ImplicitOperationError,
+ OperationThatShouldNotBePropagatedError)
from pypy.objspace.flow.model import *
from pypy.objspace.flow.framestate import (FrameState, recursively_unflatten,
recursively_flatten)
-from pypy.tool.stdlib_opcode import host_bytecode_spec
+from pypy.objspace.flow.bytecode import HostCode
class StopFlowing(Exception):
pass
@@ -50,7 +56,7 @@
def append(self, operation):
raise NotImplementedError
- def bytecode_trace(self, ec, frame):
+ def bytecode_trace(self, frame):
pass
def guessbool(self, ec, w_condition, **kwds):
@@ -72,8 +78,7 @@
raise MergeBlock(self.crnt_block, self.last_join_point)
self.crnt_block.operations.append(operation)
- def bytecode_trace(self, ec, frame):
- ec.crnt_offset = frame.last_instr # save offset for opcode
+ def bytecode_trace(self, frame):
if self.enterspamblock:
# If we have a SpamBlock, the first call to bytecode_trace()
# occurs as soon as frame.resume() starts, before interpretation
@@ -150,39 +155,15 @@
ec.recorder = self.nextreplayer
return self.booloutcome
-
-class ConcreteNoOp(Recorder):
- # In "concrete mode", no SpaceOperations between Variables are allowed.
- # Concrete mode is used to precompute lazily-initialized caches,
- # when we don't want this precomputation to show up on the flow graph.
- def append(self, operation):
- raise AssertionError, "concrete mode: cannot perform %s" % operation
-
# ____________________________________________________________
class FlowExecutionContext(ExecutionContext):
- def _init_graph(self, func, initialblock):
- # CallableFactory.pycall may add class_ to functions that are methods
- name = func.func_name
- class_ = getattr(func, 'class_', None)
- if class_ is not None:
- name = '%s.%s' % (class_.__name__, name)
- for c in "<>&!":
- name = name.replace(c, '_')
- self.graph = graph = FunctionGraph(name, initialblock)
- graph.func = func
- # attach a signature and defaults to the graph
- # so that it becomes even more interchangeable with the function
- # itself
- graph.signature = self.code.signature()
- graph.defaults = func.func_defaults or ()
-
make_link = Link # overridable for transition tracking
- def bytecode_trace(self, frame):
- self.recorder.bytecode_trace(self, frame)
+ # disable superclass method
+ bytecode_trace = None
def guessbool(self, w_condition, **kwds):
return self.recorder.guessbool(self, w_condition, **kwds)
@@ -209,46 +190,21 @@
def build_flow(self, func, constargs={}):
space = self.space
- code = PyCode._from_code(space, func.func_code)
- self.is_generator = bool(code.co_flags & CO_GENERATOR)
- self.code = code
-
- self.crnt_offset = -1
- self.frame = frame = FlowSpaceFrame(self.space, code,
- func, constargs)
+ self.frame = frame = FlowSpaceFrame(self.space, func, constargs)
self.joinpoints = {}
- initialblock = SpamBlock(frame.getstate())
- self.pendingblocks = collections.deque([initialblock])
- self._init_graph(func, initialblock)
-
- if self.is_generator:
- initialblock.operations.append(
- SpaceOperation('generator_mark', [], Variable()))
+ self.graph = frame._init_graph(func)
+ self.pendingblocks = collections.deque([self.graph.startblock])
while self.pendingblocks:
block = self.pendingblocks.popleft()
try:
self.recorder = frame.recording(block)
- except StopFlowing:
- continue # restarting a dead SpamBlock
- try:
frame.frame_finished_execution = False
+ next_instr = frame.last_instr
while True:
- w_result = frame.dispatch(frame.pycode,
- frame.last_instr,
- self)
- if frame.frame_finished_execution:
- break
- else:
- self.generate_yield(frame, w_result)
+ next_instr = frame.handle_bytecode(next_instr)
- except operation.OperationThatShouldNotBePropagatedError, e:
- raise Exception(
- 'found an operation that always raises %s: %s' % (
- self.space.unwrap(e.w_type).__name__,
- self.space.unwrap(e.get_w_value(self.space))))
-
- except operation.ImplicitOperationError, e:
+ except ImplicitOperationError, e:
if isinstance(e.w_type, Constant):
exc_cls = e.w_type.value
else:
@@ -260,11 +216,9 @@
self.recorder.crnt_block.closeblock(link)
except OperationError, e:
- #print "OE", e.w_type, e.get_w_value(self.space)
- if (self.space.do_imports_immediately and
- e.w_type is self.space.w_ImportError):
- raise ImportError('import statement always raises %s' % (
- e,))
+ if e.w_type is self.space.w_ImportError:
+ msg = 'import statement always raises %s' % e
+ raise ImportError(msg)
w_value = e.get_w_value(self.space)
link = self.make_link([e.w_type, w_value],
self.graph.exceptblock)
self.recorder.crnt_block.closeblock(link)
@@ -275,23 +229,15 @@
except MergeBlock, e:
self.mergeblock(e.block, e.currentstate)
- else:
+ except Return:
+ w_result = frame.popvalue()
assert w_result is not None
link = self.make_link([w_result], self.graph.returnblock)
self.recorder.crnt_block.closeblock(link)
- del self.recorder
+ del self.recorder
self.fixeggblocks()
- def generate_yield(self, frame, w_result):
- assert self.is_generator
- self.recorder.crnt_block.operations.append(
- SpaceOperation('yield', [w_result], Variable()))
- # we must push a dummy value that will be POPped: it's the .send()
- # passed into the generator (2.5 feature)
- assert sys.version_info >= (2, 5)
- frame.pushvalue(None)
- frame.last_instr += 1
def fixeggblocks(self):
# EggBlocks reuse the variables of their previous block,
@@ -358,15 +304,12 @@
self.pendingblocks.append(newblock)
def _convert_exc(self, operr):
- if isinstance(operr, operation.ImplicitOperationError):
+ if isinstance(operr, ImplicitOperationError):
# re-raising an implicit operation makes it an explicit one
w_value = operr.get_w_value(self.space)
operr = OperationError(operr.w_type, w_value)
return operr
- def exception_trace(self, frame, operationerr):
- pass # overridden for performance only
-
# hack for unrolling iterables, don't use this
def replace_in_stack(self, oldvalue, newvalue):
w_new = Constant(newvalue)
@@ -383,15 +326,22 @@
class FlowSpaceFrame(pyframe.CPythonFrame):
- def __init__(self, space, code, func, constargs=None):
- w_globals = Constant(func.func_globals)
- class outerfunc: pass # hack
+ def __init__(self, space, func, constargs=None):
+ code = HostCode._from_code(space, func.func_code)
+ self.pycode = code
+ self.space = space
+ self.w_globals = Constant(func.func_globals)
+ self.locals_stack_w = [None] * (code.co_nlocals + code.co_stacksize)
+ self.valuestackdepth = code.co_nlocals
+ self.lastblock = None
+
if func.func_closure is not None:
cl = [c.cell_contents for c in func.func_closure]
- outerfunc.closure = [nestedscope.Cell(Constant(value)) for value
in cl]
+ closure = [Cell(Constant(value)) for value in cl]
else:
- outerfunc.closure = None
- super(FlowSpaceFrame, self).__init__(space, code, w_globals, outerfunc)
+ closure = []
+ self.initialize_frame_scopes(closure, code)
+ self.f_lineno = code.co_firstlineno
self.last_instr = 0
if constargs is None:
@@ -402,6 +352,40 @@
arg_list[position] = Constant(value)
self.setfastscope(arg_list)
+ self.w_locals = None # XXX: only for compatibility with PyFrame
+
+ def initialize_frame_scopes(self, closure, code):
+ if not (code.co_flags & CO_NEWLOCALS):
+ raise ValueError("The code object for a function should have "
+ "the flag CO_NEWLOCALS set.")
+ if len(closure) != len(code.co_freevars):
+ raise ValueError("code object received a closure with "
+ "an unexpected number of free variables")
+ self.cells = [Cell() for _ in code.co_cellvars] + closure
+
+ def _init_graph(self, func):
+ # CallableFactory.pycall may add class_ to functions that are methods
+ name = func.func_name
+ class_ = getattr(func, 'class_', None)
+ if class_ is not None:
+ name = '%s.%s' % (class_.__name__, name)
+ for c in "<>&!":
+ name = name.replace(c, '_')
+
+ initialblock = SpamBlock(self.getstate())
+ if self.pycode.is_generator:
+ initialblock.operations.append(
+ SpaceOperation('generator_mark', [], Variable()))
+ graph = FunctionGraph(name, initialblock)
+ graph.func = func
+ # attach a signature and defaults to the graph
+ # so that it becomes even more interchangeable with the function
+ # itself
+ graph.signature = self.pycode.signature()
+ graph.defaults = func.func_defaults or ()
+ graph.is_generator = self.pycode.is_generator
+ return graph
+
def getstate(self):
# getfastscope() can return real None, for undefined locals
data = self.save_locals_stack()
@@ -413,8 +397,7 @@
data.append(self.last_exception.get_w_value(self.space))
recursively_flatten(self.space, data)
nonmergeable = (self.get_blocklist(),
- self.last_instr, # == next_instr when between bytecodes
- self.w_locals,)
+ self.last_instr) # == next_instr when between bytecodes
return FrameState(data, nonmergeable)
def setstate(self, state):
@@ -427,7 +410,7 @@
self.last_exception = None
else:
self.last_exception = OperationError(data[-2], data[-1])
- blocklist, self.last_instr, self.w_locals = state.nonmergeable
+ blocklist, self.last_instr = state.nonmergeable
self.set_blocklist(blocklist)
def recording(self, block):
@@ -448,6 +431,105 @@
prevblock = parent
return recorder
+ def handle_bytecode(self, next_instr):
+ try:
+ next_instr = self.dispatch_bytecode(next_instr)
+ except OperationThatShouldNotBePropagatedError, e:
+ raise Exception(
+ 'found an operation that always raises %s: %s' % (
+ self.space.unwrap(e.w_type).__name__,
+ self.space.unwrap(e.get_w_value(self.space))))
+ except OperationError, operr:
+ self.attach_traceback(operr)
+ next_instr = self.handle_operation_error(operr)
+ except Reraise:
+ operr = self.last_exception
+ next_instr = self.handle_operation_error(operr)
+ except RaiseWithExplicitTraceback, e:
+ next_instr = self.handle_operation_error(e.operr)
+ return next_instr
+
+ def attach_traceback(self, operr):
+ if self.pycode.hidden_applevel:
+ return
+ tb = operr.get_traceback()
+ tb = PyTraceback(self.space, self, self.last_instr, tb)
+ operr.set_traceback(tb)
+
+ def handle_operation_error(self, operr):
+ block = self.unrollstack(SApplicationException.kind)
+ if block is None:
+ # no handler found for the OperationError
+ # try to preserve the CPython-level traceback
+ import sys
+ tb = sys.exc_info()[2]
+ raise OperationError, operr, tb
+ else:
+ unroller = SApplicationException(operr)
+ next_instr = block.handle(self, unroller)
+ return next_instr
+
+ def enter_bytecode(self, next_instr):
+ self.last_instr = next_instr
+ self.space.executioncontext.recorder.bytecode_trace(self)
+
+ def dispatch_bytecode(self, next_instr):
+ while True:
+ self.enter_bytecode(next_instr)
+ next_instr, methodname, oparg = self.pycode.read(next_instr)
+ res = getattr(self, methodname)(oparg, next_instr)
+ if res is not None:
+ next_instr = res
+
+ def IMPORT_NAME(self, nameindex, next_instr):
+ space = self.space
+ modulename = self.getname_u(nameindex)
+ glob = space.unwrap(self.w_globals)
+ fromlist = space.unwrap(self.popvalue())
+ level = self.popvalue().value
+ w_obj = space.import_name(modulename, glob, None, fromlist, level)
+ self.pushvalue(w_obj)
+
+ def IMPORT_FROM(self, nameindex, next_instr):
+ w_name = self.getname_w(nameindex)
+ w_module = self.peekvalue()
+ self.pushvalue(self.space.import_from(w_module, w_name))
+
+ def RETURN_VALUE(self, oparg, next_instr):
+ w_returnvalue = self.popvalue()
+ block = self.unrollstack(SReturnValue.kind)
+ if block is None:
+ self.pushvalue(w_returnvalue) # XXX ping pong
+ raise Return
+ else:
+ unroller = SReturnValue(w_returnvalue)
+ next_instr = block.handle(self, unroller)
+ return next_instr # now inside a 'finally' block
+
+ def END_FINALLY(self, oparg, next_instr):
+ unroller = self.end_finally()
+ if isinstance(unroller, SuspendedUnroller):
+ # go on unrolling the stack
+ block = self.unrollstack(unroller.kind)
+ if block is None:
+ w_result = unroller.nomoreblocks()
+ self.pushvalue(w_result)
+ raise Return
+ else:
+ next_instr = block.handle(self, unroller)
+ return next_instr
+
+ def JUMP_ABSOLUTE(self, jumpto, next_instr):
+ return jumpto
+
+ def YIELD_VALUE(self, _, next_instr):
+ assert self.pycode.is_generator
+ w_result = self.popvalue()
+ self.space.do_operation('yield', w_result)
+ # XXX yield expressions not supported. This will blow up if the value
+ # isn't popped straightaway.
+ self.pushvalue(None)
+
def SETUP_WITH(self, offsettoend, next_instr):
# A simpler version than the 'real' 2.7 one:
# directly call manager.__enter__(), don't use special lookup functions
@@ -461,6 +543,10 @@
self.lastblock = block
self.pushvalue(w_result)
+ def LOAD_GLOBAL(self, nameindex, next_instr):
+ w_result = self.space.find_global(self.w_globals,
self.getname_u(nameindex))
+ self.pushvalue(w_result)
+
def BUILD_LIST_FROM_ARG(self, _, next_instr):
# This opcode was added with pypy-1.8. Here is a simpler
# version, enough for annotation.
@@ -488,13 +574,6 @@
def argument_factory(self, *args):
return ArgumentsForTranslation(self.space, *args)
- def handle_operation_error(self, ec, operr, *args, **kwds):
- # see test_propagate_attribute_error for why this is here
- if isinstance(operr,
operation.OperationThatShouldNotBePropagatedError):
- raise operr
- return pyframe.PyFrame.handle_operation_error(self, ec, operr,
- *args, **kwds)
-
def call_contextmanager_exit_function(self, w_func, w_typ, w_val, w_tb):
if w_typ is not self.space.w_None:
# The annotator won't allow to merge exception types with None.
diff --git a/pypy/objspace/flow/objspace.py b/pypy/objspace/flow/objspace.py
--- a/pypy/objspace/flow/objspace.py
+++ b/pypy/objspace/flow/objspace.py
@@ -5,7 +5,6 @@
import types
from pypy.tool import error
from pypy.interpreter.baseobjspace import ObjSpace, Wrappable
-from pypy.interpreter.module import Module
from pypy.interpreter.error import OperationError
from pypy.interpreter import pyframe, argument
from pypy.objspace.flow.model import *
@@ -47,24 +46,16 @@
"""
full_exceptions = False
- do_imports_immediately = True
FrameClass = flowcontext.FlowSpaceFrame
def initialize(self):
- self.concrete_mode = 1
self.w_None = Constant(None)
- self.builtin = Module(self, Constant('__builtin__'),
- Constant(__builtin__.__dict__))
- def pick_builtin(w_globals):
- return self.builtin
- self.builtin.pick_builtin = pick_builtin
- self.sys = Module(self, Constant('sys'), Constant(sys.__dict__))
- self.sys.recursionlimit = 100
+ self.builtin = Constant(__builtin__)
+ self.sys = Constant(sys)
self.w_False = Constant(False)
self.w_True = Constant(True)
self.w_type = Constant(type)
self.w_tuple = Constant(tuple)
- self.concrete_mode = 0
for exc in [KeyError, ValueError, IndexError, StopIteration,
AssertionError, TypeError, AttributeError, ImportError]:
clsname = exc.__name__
@@ -84,18 +75,9 @@
# objects which should keep their SomeObjectness
self.not_really_const = NOT_REALLY_CONST
- def enter_cache_building_mode(self):
- # when populating the caches, the flow space switches to
- # "concrete mode". In this mode, only Constants are allowed
- # and no SpaceOperation is recorded.
- previous_recorder = self.executioncontext.recorder
- self.executioncontext.recorder = flowcontext.ConcreteNoOp()
- self.concrete_mode += 1
- return previous_recorder
-
- def leave_cache_building_mode(self, previous_recorder):
- self.executioncontext.recorder = previous_recorder
- self.concrete_mode -= 1
+ # disable superclass methods
+ enter_cache_building_mode = None
+ leave_cache_building_mode = None
def is_w(self, w_one, w_two):
return self.is_true(self.is_(w_one, w_two))
@@ -104,8 +86,6 @@
id = None # real version added by add_operations()
def newdict(self, module="ignored"):
- if self.concrete_mode:
- return Constant({})
return self.do_operation('newdict')
def newtuple(self, args_w):
@@ -117,16 +97,9 @@
return Constant(tuple(content))
def newlist(self, args_w, sizehint=None):
- if self.concrete_mode:
- content = [self.unwrap(w_arg) for w_arg in args_w]
- return Constant(content)
return self.do_operation('newlist', *args_w)
def newslice(self, w_start, w_stop, w_step):
- if self.concrete_mode:
- return Constant(slice(self.unwrap(w_start),
- self.unwrap(w_stop),
- self.unwrap(w_step)))
return self.do_operation('newslice', w_start, w_stop, w_step)
def wrap(self, obj):
@@ -189,12 +162,8 @@
hasattr(to_check, '__class__') and to_check.__class__.__module__
!= '__builtin__'):
frozen = hasattr(to_check, '_freeze_') and to_check._freeze_()
if not frozen:
- if self.concrete_mode:
- # xxx do we want some warning? notice that some stuff is
harmless
- # like setitem(dict, 'n', mutable)
- pass
- else: # cannot count on it not mutating at runtime!
- raise UnwrapException
+ # cannot count on it not mutating at runtime!
+ raise UnwrapException
return obj
def interpclass_w(self, w_obj):
@@ -263,14 +232,14 @@
except error.FlowingError, a:
# attach additional source info to AnnotatorError
_, _, tb = sys.exc_info()
- formated = error.format_global_error(ec.graph, ec.crnt_offset,
+ formated = error.format_global_error(ec.graph, ec.frame.last_instr,
str(a))
e = error.FlowingError(formated)
raise error.FlowingError, e, tb
graph = ec.graph
checkgraph(graph)
- if ec.is_generator and tweak_for_generator:
+ if graph.is_generator and tweak_for_generator:
from pypy.translator.generator import tweak_generator_graph
tweak_generator_graph(graph)
return graph
@@ -302,9 +271,8 @@
# ____________________________________________________________
def do_operation(self, name, *args_w):
spaceop = SpaceOperation(name, args_w, Variable())
- if hasattr(self, 'executioncontext'): # not here during bootstrapping
- spaceop.offset = self.executioncontext.crnt_offset
- self.executioncontext.recorder.append(spaceop)
+ spaceop.offset = self.executioncontext.frame.last_instr
+ self.executioncontext.recorder.append(spaceop)
return spaceop.result
def do_operation_with_implicit_exceptions(self, name, *args_w):
@@ -366,15 +334,6 @@
if ec and w_obj is ec.frame.w_globals:
raise SyntaxError("attempt to modify global attribute %r in %r"
% (w_key, ec.graph.func))
- if self.concrete_mode:
- try:
- obj = self.unwrap_for_computation(w_obj)
- key = self.unwrap_for_computation(w_key)
- val = self.unwrap_for_computation(w_val)
- operator.setitem(obj, key, val)
- return self.w_None
- except UnwrapException:
- pass
return self.do_operation_with_implicit_exceptions('setitem', w_obj,
w_key, w_val)
@@ -407,6 +366,23 @@
return self.do_operation_with_implicit_exceptions('getattr',
w_obj, w_name)
+ def import_name(self, name, glob=None, loc=None, frm=None, level=-1):
+ try:
+ mod = __import__(name, glob, loc, frm, level)
+ except ImportError, e:
+ raise OperationError(self.w_ImportError, self.wrap(str(e)))
+ return self.wrap(mod)
+
+ def import_from(self, w_module, w_name):
+ try:
+ return self.getattr(w_module, w_name)
+ except OperationError, e:
+ if e.match(self, self.w_AttributeError):
+ raise OperationError(self.w_ImportError,
+ self.wrap("cannot import name '%s'" % w_name.value))
+ else:
+ raise
+
def call_function(self, w_func, *args_w):
nargs = len(args_w)
args = argument.ArgumentsForTranslation(self, list(args_w))
@@ -477,6 +453,18 @@
#pass
raise operation.ImplicitOperationError(w_exc_cls, w_exc_value)
+ def find_global(self, w_globals, varname):
+ try:
+ value = self.unwrap(w_globals)[varname]
+ except KeyError:
+ # not in the globals, now look in the built-ins
+ try:
+ value = getattr(self.unwrap(self.builtin), varname)
+ except AttributeError:
+ message = "global name '%s' is not defined" % varname
+ raise OperationError(self.w_NameError, self.wrap(message))
+ return self.wrap(value)
+
def w_KeyboardInterrupt(self):
# the reason to do this is: if you interrupt the flowing of a function
# with <Ctrl-C> the bytecode interpreter will raise an applevel
@@ -490,4 +478,82 @@
raise RuntimeError("the interpreter raises RuntimeError during "
"flow graph construction")
w_RuntimeError = prebuilt_recursion_error = property(w_RuntimeError)
-operation.add_operations(FlowObjSpace)
+
+def make_op(name, arity):
+ """Add function operation to the flow space."""
+ if getattr(FlowObjSpace, name, None) is not None:
+ return
+
+ op = None
+ skip = False
+ arithmetic = False
+
+ if (name.startswith('del') or
+ name.startswith('set') or
+ name.startswith('inplace_')):
+ # skip potential mutators
+ skip = True
+ elif name in ('id', 'hash', 'iter', 'userdel'):
+ # skip potential runtime context dependecies
+ skip = True
+ elif name in ('repr', 'str'):
+ rep = getattr(__builtin__, name)
+ def op(obj):
+ s = rep(obj)
+ if "at 0x" in s:
+ print >>sys.stderr, "Warning: captured address may be awkward"
+ return s
+ else:
+ op = operation.FunctionByName[name]
+ arithmetic = (name + '_ovf') in operation.FunctionByName
+
+ if not op and not skip:
+ raise ValueError("XXX missing operator: %s" % (name,))
+
+ def generic_operator(self, *args_w):
+ assert len(args_w) == arity, name + " got the wrong number of
arguments"
+ if op:
+ args = []
+ for w_arg in args_w:
+ try:
+ arg = self.unwrap_for_computation(w_arg)
+ except UnwrapException:
+ break
+ else:
+ args.append(arg)
+ else:
+ # All arguments are constants: call the operator now
+ try:
+ result = op(*args)
+ except Exception, e:
+ etype = e.__class__
+ msg = "generated by a constant operation:\n\t%s%r" % (
+ name, tuple(args))
+ raise operation.OperationThatShouldNotBePropagatedError(
+ self.wrap(etype), self.wrap(msg))
+ else:
+ # don't try to constant-fold operations giving a 'long'
+ # result. The result is probably meant to be sent to
+ # an intmask(), but the 'long' constant confuses the
+ # annotator a lot.
+ if arithmetic and type(result) is long:
+ pass
+ # don't constant-fold getslice on lists, either
+ elif name == 'getslice' and type(result) is list:
+ pass
+ # otherwise, fine
+ else:
+ try:
+ return self.wrap(result)
+ except WrapException:
+ # type cannot sanely appear in flow graph,
+ # store operation with variable result instead
+ pass
+ w_result = self.do_operation_with_implicit_exceptions(name, *args_w)
+ return w_result
+
+ setattr(FlowObjSpace, name, generic_operator)
+
+
+for (name, symbol, arity, specialnames) in ObjSpace.MethodTable:
+ make_op(name, arity)
diff --git a/pypy/objspace/flow/operation.py b/pypy/objspace/flow/operation.py
--- a/pypy/objspace/flow/operation.py
+++ b/pypy/objspace/flow/operation.py
@@ -304,83 +304,3 @@
_add_exceptions("""pow""",
OverflowError) # for the float case
del _add_exceptions, _add_except_ovf
-
-def make_op(fs, name, symbol, arity, specialnames):
- if getattr(fs, name, None) is not None:
- return
-
- op = None
- skip = False
- arithmetic = False
-
- if (name.startswith('del') or
- name.startswith('set') or
- name.startswith('inplace_')):
- # skip potential mutators
- skip = True
- elif name in ('id', 'hash', 'iter', 'userdel'):
- # skip potential runtime context dependecies
- skip = True
- elif name in ('repr', 'str'):
- rep = getattr(__builtin__, name)
- def op(obj):
- s = rep(obj)
- if "at 0x" in s:
- print >>sys.stderr, "Warning: captured address may be awkward"
- return s
- else:
- op = FunctionByName[name]
- arithmetic = (name + '_ovf') in FunctionByName
-
- if not op and not skip:
- raise ValueError("XXX missing operator: %s" % (name,))
-
- def generic_operator(self, *args_w):
- assert len(args_w) == arity, name + " got the wrong number of
arguments"
- if op:
- args = []
- for w_arg in args_w:
- try:
- arg = self.unwrap_for_computation(w_arg)
- except model.UnwrapException:
- break
- else:
- args.append(arg)
- else:
- # All arguments are constants: call the operator now
- try:
- result = op(*args)
- except Exception, e:
- etype = e.__class__
- msg = "generated by a constant operation:\n\t%s%r" % (
- name, tuple(args))
- raise OperationThatShouldNotBePropagatedError(
- self.wrap(etype), self.wrap(msg))
- else:
- # don't try to constant-fold operations giving a 'long'
- # result. The result is probably meant to be sent to
- # an intmask(), but the 'long' constant confuses the
- # annotator a lot.
- if arithmetic and type(result) is long:
- pass
- # don't constant-fold getslice on lists, either
- elif name == 'getslice' and type(result) is list:
- pass
- # otherwise, fine
- else:
- try:
- return self.wrap(result)
- except model.WrapException:
- # type cannot sanely appear in flow graph,
- # store operation with variable result instead
- pass
- w_result = self.do_operation_with_implicit_exceptions(name, *args_w)
- return w_result
-
- setattr(fs, name, generic_operator)
-
-
-def add_operations(fs):
- """Add function operations to the flow space."""
- for line in ObjSpace.MethodTable:
- make_op(fs, *line)
diff --git a/pypy/objspace/flow/specialcase.py
b/pypy/objspace/flow/specialcase.py
--- a/pypy/objspace/flow/specialcase.py
+++ b/pypy/objspace/flow/specialcase.py
@@ -11,31 +11,8 @@
args_w, kwds_w = args.unpack()
assert kwds_w == {}, "should not call %r with keyword arguments" % (fn,)
assert len(args_w) > 0 and len(args_w) <= 5, 'import needs 1 to 5
arguments'
- w_name = args_w[0]
- w_None = space.wrap(None)
- w_glob, w_loc, w_frm = w_None, w_None, w_None
- if len(args_w) > 1:
- w_glob = args_w[1]
- if len(args_w) > 2:
- w_loc = args_w[2]
- if len(args_w) > 3:
- w_frm = args_w[3]
- if not isinstance(w_loc, Constant):
- # import * in a function gives us the locals as Variable
- # we always forbid it as a SyntaxError
- raise SyntaxError, "RPython: import * is not allowed in functions"
- if space.do_imports_immediately:
- name, glob, loc, frm = (space.unwrap(w_name), space.unwrap(w_glob),
- space.unwrap(w_loc), space.unwrap(w_frm))
- try:
- mod = __import__(name, glob, loc, frm)
- except ImportError, e:
- raise OperationError(space.w_ImportError, space.wrap(str(e)))
- return space.wrap(mod)
- # redirect it, but avoid exposing the globals
- w_glob = Constant({})
- return space.do_operation('simple_call', Constant(__import__),
- w_name, w_glob, w_loc, w_frm)
+ args = [space.unwrap(arg) for arg in args_w]
+ return space.import_name(*args)
def sc_operator(space, fn, args):
args_w, kwds_w = args.unpack()
diff --git a/pypy/objspace/flow/test/test_framestate.py
b/pypy/objspace/flow/test/test_framestate.py
--- a/pypy/objspace/flow/test/test_framestate.py
+++ b/pypy/objspace/flow/test/test_framestate.py
@@ -10,14 +10,11 @@
cls.space = FlowObjSpace()
def getframe(self, func):
- space = self.space
try:
func = func.im_func
except AttributeError:
pass
- code = func.func_code
- code = PyCode._from_code(self.space, code)
- frame = FlowSpaceFrame(space, code, func)
+ frame = FlowSpaceFrame(self.space, func)
# hack the frame
frame.locals_stack_w[frame.pycode.co_nlocals-1] = Constant(None)
return frame
diff --git a/pypy/objspace/flow/test/test_objspace.py
b/pypy/objspace/flow/test/test_objspace.py
--- a/pypy/objspace/flow/test/test_objspace.py
+++ b/pypy/objspace/flow/test/test_objspace.py
@@ -701,6 +701,13 @@
from pypy import this_does_not_exist
py.test.raises(ImportError, 'self.codetest(f)')
+ def test_relative_import(self):
+ def f():
+ from ..test.test_objspace import FlowObjSpace
+ # Check that the function works in Python
+ assert f() is None
+ self.codetest(f)
+
def test_mergeable(self):
def myfunc(x):
if x:
@@ -987,16 +994,14 @@
pass
py.test.raises(error.FlowingError, "self.codetest(f)")
-
-class TestFlowObjSpaceDelay(Base):
- def setup_class(cls):
- cls.space = FlowObjSpace()
- cls.space.do_imports_immediately = False
-
- def test_import_something(self):
+ def test_locals_dict(self):
def f():
- from some.unknown.module import stuff
- g = self.codetest(f)
+ x = 5
+ return x
+ exec "None"
+ graph = self.codetest(f)
+ assert len(graph.startblock.exits) == 1
+ assert graph.startblock.exits[0].target == graph.returnblock
DATA = {'x': 5,
diff --git a/pypy/rpython/lltypesystem/rbuiltin.py
b/pypy/rpython/lltypesystem/rbuiltin.py
--- a/pypy/rpython/lltypesystem/rbuiltin.py
+++ b/pypy/rpython/lltypesystem/rbuiltin.py
@@ -16,7 +16,7 @@
v_obj, v_typ = hop.inputargs(pyobj_repr, pyobj_repr)
c = hop.inputconst(pyobj_repr, isinstance)
v = hop.genop('simple_call', [c, v_obj, v_typ], resulttype =
pyobj_repr)
- return hop.llops.convertvar(v, pyobj_repr, bool_repr)
+ return hop.llops.convertvar(v, pyobj_repr, bool_repr)
if hop.args_s[1].is_constant() and hop.args_s[1].const == list:
if hop.args_s[0].knowntype != list:
@@ -58,17 +58,10 @@
return hop.llops.convertvar(v, pyobj_repr, bool_repr)
raise TyperError("hasattr is only suported on a constant or on PyObject")
-def rtype_builtin___import__(hop):
- xxx # should not be used any more
- args_v = hop.inputargs(*[pyobj_repr for ign in hop.args_r])
- c = hop.inputconst(pyobj_repr, __import__)
- return hop.genop('simple_call', [c] + args_v, resulttype = pyobj_repr)
-
BUILTIN_TYPER = {}
BUILTIN_TYPER[objectmodel.instantiate] = rtype_instantiate
BUILTIN_TYPER[isinstance] = rtype_builtin_isinstance
BUILTIN_TYPER[hasattr] = rtype_builtin_hasattr
-BUILTIN_TYPER[__import__] = rtype_builtin___import__
BUILTIN_TYPER[objectmodel.r_dict] = rtype_r_dict
# _________________________________________________________________
diff --git a/pypy/translator/translator.py b/pypy/translator/translator.py
--- a/pypy/translator/translator.py
+++ b/pypy/translator/translator.py
@@ -66,12 +66,6 @@
log.start(nice_repr_for_func(func))
from pypy.objspace.flow.objspace import FlowObjSpace
space = FlowObjSpace(self.flowconfig)
- if self.annotator:
- # ZZZ
- self.annotator.policy._adjust_space_config(space)
- elif hasattr(self, 'no_annotator_but_do_imports_immediately'):
- space.do_imports_immediately = (
- self.no_annotator_but_do_imports_immediately)
graph = space.build_flow(func)
if self.config.translation.simplifying:
simplify.simplify_graph(graph)
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit