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