[pypy-svn] r25681 - pypy/dist/pypy/objspace/flow
tismer at codespeak.net
tismer at codespeak.net
Tue Apr 11 01:54:01 CEST 2006
Author: tismer
Date: Tue Apr 11 01:53:58 2006
New Revision: 25681
Modified:
pypy/dist/pypy/objspace/flow/objspace.py
Log:
a more or less small addition to flow space: Automatic decision if an import is immediate or not.
The implementation gave me quite a good time until I found out how to do this.
But now we should be able to drop 'do_imports_immediately' by using 'deferred constantness'
(The concept is documented inside :-)
Modified: pypy/dist/pypy/objspace/flow/objspace.py
==============================================================================
--- pypy/dist/pypy/objspace/flow/objspace.py (original)
+++ pypy/dist/pypy/objspace/flow/objspace.py Tue Apr 11 01:53:58 2006
@@ -63,9 +63,8 @@
#self.make_sys()
# objects which should keep their SomeObjectness
self.not_really_const = NOT_REALLY_CONST
- # variables which might in turn turn into constants.
- # purpose: allow for importing into globals.
- self.maybe_const = {} # variable -> constant
+ # tracking variables which might in turn turn into constants.
+ self.const_tracker = None
def enter_cache_building_mode(self):
# when populating the caches, the flow space switches to
@@ -168,7 +167,8 @@
to_check = obj
if hasattr(to_check, 'im_self'):
to_check = to_check.im_self
- if (not isinstance(to_check, (type, types.ClassType)) and # classes/types are assumed immutable
+ if (not isinstance(to_check, (type, types.ClassType, types.ModuleType)) and
+ # classes/types/modules are assumed immutable
hasattr(to_check, '__class__') and to_check.__class__.__module__ != '__builtin__'):
frozen = hasattr(to_check, '_freeze_') and to_check._freeze_()
if not frozen:
@@ -594,9 +594,53 @@
for line in ObjSpace.MethodTable:
make_op(*line)
-# override getattr for not really const objects
+"""
+Strategy for a new import logic
+-------------------------------
+
+It is an old problem to decide whether to use do_imports_immediately.
+In general, it would be nicer not to use this flag for RPython, in order
+to make it easy to support imports at run-time for extensions.
+
+On the other hand, there are situations where this is absolutely needed:
+Some of the ll helper functions need to import something late, to
+avoid circular imports. Not doing the import immediately would cause
+a crash, because the imported object would become SomeObject.
+
+We would like to have control over imports even on a per-import policy.
+
+As a general solution, I came up with the following trick, or maybe it's
+not a trick but a good concept:
+
+By declaring the imported subject as a global, you trigger the immediate
+import. This is consistent with the RPython concept that globals
+should never change, just with the addition that objects may be added.
+In addition, we consider global modules to be immutable, making attribute
+access a constant operation.
+
+As a generalisation, we can enforce that getattr/setattr on any
+object that is unwrappable for computation is evaluated
+immediately. This gives us early detection of programming errors.
+XXX this step isn't done, yet, need to discuss this.
+
+Implementation
+--------------
+
+It is not completely trivial, since we have to intercept the process
+of flowing, to keep trak of which variable might become a constant.
+Finally I ended up with a rather simple solution:
+Flowcontext monitors every link creation, by no longer using
+Link() directly, but redirecting this to a function make_link,
+which can be patched to record the creation of links.
+
+The actual tracking and constant resolving is implemented in the
+ConstTracker class below.
+
+"""
def override():
+ from __builtin__ import getattr as _getattr # uhmm
+
def getattr(self, w_obj, w_name):
# handling special things like sys
# (maybe this will vanish with a unique import logic)
@@ -604,23 +648,90 @@
const_w = self.not_really_const[w_obj]
if w_name not in const_w:
return self.do_operation_with_implicit_exceptions('getattr', w_obj, w_name)
- # tracking variables which might be constants
- return self.regular_getattr(w_obj, w_name)
+ w_res = self.regular_getattr(w_obj, w_name)
+ # tracking variables which might be(come) constants
+ if self.const_tracker:
+ self.track_possible_constant(w_res, _getattr, w_obj, w_name)
+ return w_res
FlowObjSpace.regular_getattr = FlowObjSpace.getattr
FlowObjSpace.getattr = getattr
- # protect us from globals access
+ # protect us from globals access but support constant import into globals
def setitem(self, w_obj, w_key, w_val):
ec = self.getexecutioncontext()
if not (ec and w_obj is ec.w_globals):
return self.regular_setitem(w_obj, w_key, w_val)
- raise SyntaxError, "attempt to write global attribute %r in %r" % (w_key, ec.graph.func)
+ globals = self.unwrap(w_obj)
+ try:
+ key = self.unwrap_for_computation(self.resolve_constant(w_key))
+ val = self.unwrap_for_computation(self.resolve_constant(w_val))
+ if key not in globals or val == globals[key]:
+ globals[key] = val
+ return self.w_None
+ except UnwrapException:
+ pass
+ raise SyntaxError, "attempt to modify global attribute %r in %r" % (w_key, ec.graph.func)
FlowObjSpace.regular_setitem = FlowObjSpace.setitem
FlowObjSpace.setitem = setitem
+ def track_possible_constant(self, w_ret, func, *args_w):
+ if not self.const_tracker:
+ self.const_tracker = ConstTracker(self)
+ tracker = self.const_tracker
+ tracker.track_call(w_ret, func, *args_w)
+ self.getexecutioncontext().start_monitoring(tracker.monitor_transition)
+
+ FlowObjSpace.track_possible_constant = track_possible_constant
+
+ def resolve_constant(self, w_obj):
+ if self.const_tracker:
+ w_obj = self.const_tracker.resolve_const(w_obj)
+ return w_obj
+
+ FlowObjSpace.resolve_constant = resolve_constant
+
override()
+
+class ConstTracker(object):
+ def __init__(self, space):
+ assert isinstance(space, FlowObjSpace)
+ self.space = space
+ self.known_consts = {}
+ self.tracked_vars = {}
+ self.mapping = {}
+
+ def track_call(self, w_res, callable, *args_w):
+ """ defer evaluation of this expression until a const is needed
+ """
+ self.mapping[w_res] = w_res
+ self.tracked_vars[w_res] = callable, args_w
+
+ def monitor_transition(self, link):
+ for vin, vout in zip(link.args, link.target.inputargs):
+ # we record all true transitions, but no cycles.
+ if vin in self.mapping and vout not in self.mapping:
+ # the mapping leads directly to the origin.
+ self.mapping[vout] = self.mapping[vin]
+
+ def resolve_const(self, w_obj):
+ """ compute a latent constant expression """
+ if isinstance(w_obj, Constant):
+ return w_obj
+ w = self.mapping.get(w_obj, w_obj)
+ if w in self.known_consts:
+ return self.known_consts[w]
+ if w not in self.tracked_vars:
+ raise SyntaxError, 'RPython: cannot compute a constant for %s in %s' % (
+ w_obj, self.space.getexecutioncontext().graph.func)
+ callable, args_w = self.tracked_vars.pop(w)
+ args_w = [self.resolve_const(w_x) for w_x in args_w]
+ args = [self.space.unwrap_for_computation(w_x) for w_x in args_w]
+ w_ret = self.space.wrap(callable(*args))
+ self.known_consts[w] = w_ret
+ return w_ret
+
# ______________________________________________________________________
# End of objspace.py
More information about the Pypy-commit
mailing list