[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