[Zope-Checkins] CVS: Zope/lib/python/RestrictedPython/compiler_2_1 - misc.py:1.2.76.1 pyassem.py:1.2.76.1 symbols.py:1.2.76.1 transformer.py:1.2.76.1 visitor.py:1.2.76.1

Shane Hathaway shane@cvs.zope.org
Thu, 8 Aug 2002 14:49:26 -0400


Update of /cvs-repository/Zope/lib/python/RestrictedPython/compiler_2_1
In directory cvs.zope.org:/tmp/cvs-serv20389/compiler_2_1

Modified Files:
      Tag: shane-leaks-repair-branch
	misc.py pyassem.py symbols.py transformer.py visitor.py 
Log Message:
With much help from Cyclops, removed all the cyclic references in the compiler
package that can be discovered by the unit tests.  Used weak references in most
places, _break_cr() methods in other places, and even a "tuple lookalike" in
one place.

There may be other cycles, but they aren't likely to be so frequent.

Note that this corrects only the Python 2.1 version of the compiler.
Python 2.2's compiler will have to be fixed independently.


=== Zope/lib/python/RestrictedPython/compiler_2_1/misc.py 1.2 => 1.2.76.1 ===
--- Zope/lib/python/RestrictedPython/compiler_2_1/misc.py:1.2	Fri Dec 21 14:34:48 2001
+++ Zope/lib/python/RestrictedPython/compiler_2_1/misc.py	Thu Aug  8 14:49:25 2002
@@ -1,4 +1,5 @@
 import types
+from weakref import WeakKeyDictionary, ref
 
 def flatten(tup):
     elts = []
@@ -28,6 +29,28 @@
         c = Set()
         c.elts.update(self.elts)
         return c
+
+
+class WeakSet (WeakKeyDictionary):
+    __contains__ = WeakKeyDictionary.has_key
+    has_elt = WeakKeyDictionary.has_key
+    elements = WeakKeyDictionary.keys
+
+    # __init__ is based on Python 2.2's WeakDictionary implementation.
+    def __init__(self):
+        self.data = {}
+        def remove(k, selfref=ref(self)):
+            self = selfref()
+            if self is not None:
+                del self.data[k]
+        self._remove = remove
+
+    def add(self, elt):
+        self[elt] = 1
+
+    def remove(self, key):
+        del self.data[ref(key)]
+
 
 class Stack:
     def __init__(self):


=== Zope/lib/python/RestrictedPython/compiler_2_1/pyassem.py 1.2 => 1.2.76.1 ===
--- Zope/lib/python/RestrictedPython/compiler_2_1/pyassem.py:1.2	Fri Dec 21 14:34:48 2001
+++ Zope/lib/python/RestrictedPython/compiler_2_1/pyassem.py	Thu Aug  8 14:49:25 2002
@@ -7,6 +7,7 @@
 import string
 import sys
 import types
+from weakref import ref
 
 import misc
 from consts import CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, CO_VARKEYWORDS
@@ -231,8 +232,8 @@
 
     def __init__(self, label=''):
         self.insts = []
-        self.inEdges = misc.Set()
-        self.outEdges = misc.Set()
+        self.inEdges = misc.WeakSet()
+        self.outEdges = misc.WeakSet()
         self.label = label
         self.bid = Block._count
         self.next = []
@@ -253,6 +254,7 @@
         op = inst[0]
         if op[:4] == 'JUMP':
             self.outEdges.add(inst[1])
+            inst = BlockRefInstruction(inst)
         self.insts.append(inst)
 
     def getInstructions(self):
@@ -311,6 +313,23 @@
             if hasattr(op, 'graph'):
                 contained.append(op.graph)
         return contained
+        
+
+class BlockRefInstruction:
+    """Tuple-like object that makes a weak reference to the block."""
+
+    def __init__(self, inst):
+        self._data = (inst[0], ref(inst[1]),) + inst[2:]
+
+    def __len__(self):
+        return len(self._data)
+
+    def __getitem__(self, index):
+        if index == 1:
+            return self._data[1]()
+        else:
+            return self._data[index]
+
 
 # flags for code objects
 
@@ -403,6 +422,20 @@
         if io:
             sys.stdout = save
 
+    def _max_depth(self, b, d, depth, seen):
+        if seen.has_key(b):
+            return d
+        seen[b] = 1
+        d = d + depth[b]
+        children = b.get_children()
+        if children:
+            return max([self._max_depth(c, d, depth, seen) for c in children])
+        else:
+            if not b.label == "exit":
+                return self._max_depth(self.exit, d, depth, seen)
+            else:
+                return d
+
     def computeStackDepth(self):
         """Compute the max stack depth.
 
@@ -417,21 +450,7 @@
 
         seen = {}
 
-        def max_depth(b, d):
-            if seen.has_key(b):
-                return d
-            seen[b] = 1
-            d = d + depth[b]
-            children = b.get_children()
-            if children:
-                return max([max_depth(c, d) for c in children])
-            else:
-                if not b.label == "exit":
-                    return max_depth(self.exit, d)
-                else:
-                    return d
-
-        self.stacksize = max_depth(self.entry, 0)
+        self.stacksize = self._max_depth(self.entry, 0, depth, seen)
 
     def flattenGraph(self):
         """Arrange the blocks in order and resolve jumps"""


=== Zope/lib/python/RestrictedPython/compiler_2_1/symbols.py 1.2 => 1.2.76.1 ===
--- Zope/lib/python/RestrictedPython/compiler_2_1/symbols.py:1.2	Fri Dec 21 14:34:48 2001
+++ Zope/lib/python/RestrictedPython/compiler_2_1/symbols.py	Thu Aug  8 14:49:25 2002
@@ -4,6 +4,7 @@
 from consts import SC_LOCAL, SC_GLOBAL, SC_FREE, SC_CELL, SC_UNKNOWN
 from misc import mangle
 import types
+from weakref import proxy
 
 import sys
 
@@ -13,7 +14,7 @@
     # XXX how much information do I need about each name?
     def __init__(self, name, module, klass=None):
         self.name = name
-        self.module = module
+        self.module = proxy(module)
         self.defs = {}
         self.uses = {}
         self.globals = {}


=== Zope/lib/python/RestrictedPython/compiler_2_1/transformer.py 1.2 => 1.2.76.1 ===
--- Zope/lib/python/RestrictedPython/compiler_2_1/transformer.py:1.2	Fri Dec 21 14:34:48 2001
+++ Zope/lib/python/RestrictedPython/compiler_2_1/transformer.py	Thu Aug  8 14:49:25 2002
@@ -43,7 +43,10 @@
     return parse(src)
 
 def parse(buf):
-    return Transformer().parsesuite(buf)
+    t = Transformer()
+    res = t.parsesuite(buf)
+    t._break_cr()
+    return res
 
 def asList(nodes):
     l = []
@@ -1198,6 +1201,11 @@
         if n in _doc_nodes and len(node) == 1:
             return self.get_docstring(node[0])
         return None
+
+    def _break_cr(self):
+        # Break circular references for faster garbage collection
+        del self._dispatch
+        del self._atom_dispatch
 
 
 _doc_nodes = [


=== Zope/lib/python/RestrictedPython/compiler_2_1/visitor.py 1.2 => 1.2.76.1 ===
--- Zope/lib/python/RestrictedPython/compiler_2_1/visitor.py:1.2	Fri Dec 21 14:34:48 2001
+++ Zope/lib/python/RestrictedPython/compiler_2_1/visitor.py	Thu Aug  8 14:49:25 2002
@@ -71,6 +71,14 @@
 
     _preorder = dispatch
 
+    def _break_cr(self):
+        # Break circular references for faster garbage collection
+        del self.node
+        del self._cache
+        del self.visitor.visit
+        del self.visitor
+
+
 class ExampleASTVisitor(ASTVisitor):
     """Prints examples of the nodes that aren't visited
 
@@ -110,7 +118,9 @@
     if verbose is not None:
         w.VERBOSE = verbose
     w.preorder(tree, visitor)
-    return w.visitor
+    res = w.visitor
+    w._break_cr()
+    return res
 
 def dumpNode(node):
     print node.__class__