Author: Maciej Fijalkowski <[email protected]>
Branch: lightweight-finalizers
Changeset: r47884:ef34d4cdce31
Date: 2011-10-09 01:28 +0200
http://bitbucket.org/pypy/pypy/changeset/ef34d4cdce31/

Log:    add FinalizerAnalyzer that checks whether an RPython del is
        lightweight

diff --git a/pypy/translator/backendopt/finalizer.py 
b/pypy/translator/backendopt/finalizer.py
new file mode 100644
--- /dev/null
+++ b/pypy/translator/backendopt/finalizer.py
@@ -0,0 +1,31 @@
+
+from pypy.translator.backendopt import graphanalyze
+from pypy.rpython.lltypesystem import lltype
+
+class FinalizerAnalyzer(graphanalyze.BoolGraphAnalyzer):
+    """ Analyzer that determines whether a finalizer is lightweight enough
+    so it can be called without all the complicated logic in the garbage
+    collector. The set of operations here is restrictive for a good reason
+    - it's better to be safe. Specifically disallowed operations:
+
+    * anything that escapes self
+    * anything that can allocate
+    """
+    ok_operations = ['getfield', 'ptr_nonzero', 'free', 'same_as',
+                     'direct_ptradd', 'force_cast', 'cast_primitive',
+                     'cast_pointer']
+    
+    def analyze_simple_operation(self, op, graphinfo):
+        if op.opname in self.ok_operations:
+            return self.bottom_result()
+        if op.opname.startswith('int_') or op.opname.startswith('float_'):
+            return self.bottom_result()
+        if op.opname == 'setfield':
+            TP = op.args[2].concretetype
+            if not isinstance(TP, lltype.Ptr) or TP.TO._gckind == 'raw':
+                # primitive type
+                return self.bottom_result()
+        print op
+        import pdb
+        pdb.set_trace()
+        return self.top_result()
diff --git a/pypy/translator/backendopt/test/test_finalizer.py 
b/pypy/translator/backendopt/test/test_finalizer.py
new file mode 100644
--- /dev/null
+++ b/pypy/translator/backendopt/test/test_finalizer.py
@@ -0,0 +1,94 @@
+
+import py
+from pypy.translator.backendopt.finalizer import FinalizerAnalyzer
+from pypy.translator.translator import TranslationContext, graphof
+from pypy.translator.backendopt.all import backend_optimizations
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.conftest import option
+
+
+class BaseFinalizerAnalyzerTests(object):
+    """ Below are typical destructors that we encounter in pypy
+    """
+
+    type_system = None
+    
+    def analyze(self, func, sig, func_to_analyze=None, backendopt=False):
+        if func_to_analyze is None:
+            func_to_analyze = func
+        t = TranslationContext()
+        t.buildannotator().build_types(func, sig)
+        t.buildrtyper(type_system=self.type_system).specialize()
+        if backendopt:
+            backend_optimizations(t)
+        if option.view:
+            t.view()
+        a = FinalizerAnalyzer(t)
+        fgraph = graphof(t, func_to_analyze)
+        result = a.analyze_direct_call(fgraph)
+        return result
+
+    def test_nothing(self):
+        def f():
+            pass
+        r = self.analyze(f, [])
+        assert not r
+
+
+class TestLLType(BaseFinalizerAnalyzerTests):
+    type_system = 'lltype'
+
+    def test_malloc(self):
+        S = lltype.GcStruct('S')
+        
+        def f():
+            return lltype.malloc(S)
+
+        r = self.analyze(f, [])
+        assert r
+
+    def test_raw_free_getfield(self):
+        S = lltype.Struct('S')
+        
+        class A(object):
+            def __init__(self):
+                self.x = lltype.malloc(S, flavor='raw')
+
+            def __del__(self):
+                if self.x:
+                    self.x = lltype.nullptr(S)
+                    lltype.free(self.x, flavor='raw')
+
+        def f():
+            return A()
+
+        r = self.analyze(f, [], A.__del__.im_func)
+        assert not r
+
+    def test_c_call(self):
+        C = rffi.CArray(lltype.Signed)
+        c = rffi.llexternal('x', [lltype.Ptr(C)], lltype.Signed)
+
+        def g():
+            p = lltype.malloc(C, 3, flavor='raw')
+            f(p)
+        
+        def f(p):
+            c(rffi.ptradd(p, 0))
+            lltype.free(p, flavor='raw')
+
+        r = self.analyze(g, [], f, backendopt=True)
+        assert not r
+
+    def test_os_call(self):
+        py.test.skip("can allocate OSError, but also can raise, ignore for 
now")
+        import os
+        
+        def f(i):
+            os.close(i)
+
+        r = self.analyze(f, [int], backendopt=True)
+        assert not r
+
+class TestOOType(BaseFinalizerAnalyzerTests):
+    type_system = 'ootype'
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to