diff --git a/llvm/__init__.py b/llvm/__init__.py index c800500..c5e1123 100644 --- a/llvm/__init__.py +++ b/llvm/__init__.py @@ -6,6 +6,8 @@ __version__ = '0.9.1' from weakref import WeakValueDictionary +from llvm._utils.finalizer import track as track_resource +from llvm._utils.finalizer import OwnerMixin as _OwnerMixin #===----------------------------------------------------------------------=== @@ -19,11 +21,26 @@ class LLVMException(Exception): Exception.__init__(self, msg) +#===----------------------------------------------------------------------=== +# Resource Handle +#===----------------------------------------------------------------------=== + +class Handle(_OwnerMixin): + _finalizer = NotImplemented + + def __init__(self, ptr): + self.ptr = ptr + self._finalizer_track(self.ptr) + + @classmethod + def _finalize(cls, ptr): + cls._finalizer(ptr) + #===----------------------------------------------------------------------=== # Ownables #===----------------------------------------------------------------------=== -class Ownable(object): +class Ownable(Handle): """Objects that can be owned. Modules and Module Providers can be owned, i.e., the responsibility of @@ -32,25 +49,21 @@ class Ownable(object): is NOT intended for public use. """ - def __init__(self, ptr, del_fn): - self.ptr = ptr + def __init__(self, ptr): + Handle.__init__(self, ptr) self.owner = None - self.del_fn = del_fn def _own(self, owner): if self.owner: raise LLVMException("object already owned") self.owner = owner + self._finalizer_untrack(self.ptr) def _disown(self): if not self.owner: raise LLVMException("not owned") self.owner = None - - def __del__(self): - if not self.owner: - self.del_fn(self.ptr) - + self._finalizer_track(self.ptr) #===----------------------------------------------------------------------=== # Dummy owner, will not delete ownee. Be careful. diff --git a/llvm/_utils/__init__.py b/llvm/_utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/llvm/_utils/finalizer.py b/llvm/_utils/finalizer.py new file mode 100644 index 0000000..56df5b7 --- /dev/null +++ b/llvm/_utils/finalizer.py @@ -0,0 +1,58 @@ +''' +Modified C-level finalizer by Benjamin Peterson +Available at http://code.activestate.com/recipes/577242-calling-c-level-finalizers-without-__del__/ +''' +import sys +import traceback +import weakref + +class OwnerRef(weakref.ref): + """A simple weakref.ref subclass, so attributes can be added.""" + pass + +def _run_finalizer(ref): + """Internal weakref callback to run finalizers""" + del _finalize_refs[ref.owner] + for item, finalizer in ref.items: + try: + finalizer(item) + except Exception: + print>>sys.stderr, "Exception running {}:".format(finalizer) + traceback.print_exc() + +_finalize_refs = {} + +def track(owner, item, finalizer): + """Register an object for finalization. + + ``owner`` is the the object which is responsible for ``item``. + ``finalizer`` will be called with ``item`` as its only argument when + ``owner`` is destroyed by the garbage collector. + """ + if id(owner) in _finalize_refs: + ref = _finalize_refs[id(owner)] + else: + ref = OwnerRef(owner, _run_finalizer) + ref.owner = id(owner) + ref.items = [] + ref.items.append((item, finalizer)) + _finalize_refs[id(owner)] = ref + +def untrack(owner, item): + ref = _finalize_refs[id(owner)] + # search and remove the item from the ref + for i, (item_i, _) in enumerate(ref.items): + if item_i is item: + del ref.items[i] + # remove the ref if it is empty + if not ref.items: + del _finalize_refs[id(owner)] + +class OwnerMixin(object): + def _finalizer_track(self, item): + if not hasattr(self, '_finalize'): + raise AttributeError("%s must define a _finalize method" % self) + track(self, item, type(self)._finalize) + + def _finalizer_untrack(self, item): + untrack(self, item) diff --git a/llvm/core.py b/llvm/core.py index 6a7cc1d..6d3a088 100644 --- a/llvm/core.py +++ b/llvm/core.py @@ -396,7 +396,9 @@ class Module(llvm.Ownable, llvm.Cacheable): Use the static method `Module.new' instead. """ - llvm.Ownable.__init__(self, ptr, _core.LLVMDisposeModule) + llvm.Ownable.__init__(self, ptr) + + _finalizer = _core.LLVMDisposeModule def __str__(self): """Text representation of a module. @@ -1798,7 +1800,7 @@ def _make_value(ptr): # Builder #===----------------------------------------------------------------------=== -class Builder(object): +class Builder(llvm.Handle): @staticmethod def new(basic_block): @@ -1808,10 +1810,9 @@ class Builder(object): return b def __init__(self, ptr): - self.ptr = ptr + llvm.Handle.__init__(self, ptr) - def __del__(self): - _core.LLVMDisposeBuilder(self.ptr) + _finalizer = _core.LLVMDisposeBuilder def position_at_beginning(self, bblk): """Position the builder at the beginning of the given block. @@ -2300,7 +2301,7 @@ class Builder(object): # Memory buffer #===----------------------------------------------------------------------=== -class MemoryBuffer(object): +class MemoryBuffer(llvm.Handle): @staticmethod def from_file(fname): @@ -2321,10 +2322,9 @@ class MemoryBuffer(object): return (obj, "") def __init__(self, ptr): - self.ptr = ptr + llvm.Handle.__init__(self, ptr) - def __del__(self): - _core.LLVMDisposeMemoryBuffer(self.ptr) + _finalizer = _core.LLVMDisposeMemoryBuffer #===----------------------------------------------------------------------=== diff --git a/llvm/ee.py b/llvm/ee.py index d522473..27f6018 100644 --- a/llvm/ee.py +++ b/llvm/ee.py @@ -56,7 +56,7 @@ BO_LITTLE_ENDIAN = 1 # Generic value #===----------------------------------------------------------------------=== -class GenericValue(object): +class GenericValue(llvm.Handle): @staticmethod def int(ty, intval): @@ -99,10 +99,9 @@ class GenericValue(object): return GenericValue(ptr) def __init__(self, ptr): - self.ptr = ptr + llvm.Handle.__init__(self, ptr) - def __del__(self): - _core.LLVMDisposeGenericValue(self.ptr) + _finalizer = _core.LLVMDisposeGenericValue def as_int(self): return _core.LLVMGenericValueToInt(self.ptr, 0) @@ -128,7 +127,7 @@ def _unpack_generic_values(objlist): # Engine builder #===----------------------------------------------------------------------=== -class EngineBuilder(object): +class EngineBuilder(llvm.Handle): @staticmethod def new(module): core.check_is_module(module) @@ -137,11 +136,10 @@ class EngineBuilder(object): return EngineBuilder(obj, module) def __init__(self, ptr, module): - self.ptr = ptr + llvm.Handle.__init__(self, ptr) self._module = module - def __del__(self): - _core.LLVMDisposeEngineBuilder(self.ptr) + _finalizer = _core.LLVMDisposeEngineBuilder def force_jit(self): _core.LLVMEngineBuilderForceJIT(self.ptr) @@ -193,7 +191,7 @@ class EngineBuilder(object): # Execution engine #===----------------------------------------------------------------------=== -class ExecutionEngine(object): +class ExecutionEngine(llvm.Handle): @staticmethod def new(module, force_interpreter=False): @@ -205,11 +203,10 @@ class ExecutionEngine(object): return ExecutionEngine(ret, module) def __init__(self, ptr, module): - self.ptr = ptr + llvm.Handle.__init__(self, ptr) module._own(self) - def __del__(self): - _core.LLVMDisposeExecutionEngine(self.ptr) + _finalizer = _core.LLVMDisposeExecutionEngine def run_function(self, fn, args): core.check_is_function(fn) @@ -299,7 +296,9 @@ class TargetMachine(llvm.Ownable): return TargetMachine(ptr) def __init__(self, ptr): - llvm.Ownable.__init__(self, ptr, _core.LLVMDisposeTargetMachine) + llvm.Ownable.__init__(self, ptr) + + _finalizer = _core.LLVMDisposeTargetMachine def emit_assembly(self, module): '''returns byte string of the module as assembly code of the target machine diff --git a/llvm/passes.py b/llvm/passes.py index 657aabd..bca8caf 100644 --- a/llvm/passes.py +++ b/llvm/passes.py @@ -45,16 +45,15 @@ import warnings # Pass manager builder #===----------------------------------------------------------------------=== -class PassManagerBuilder(object): +class PassManagerBuilder(llvm.Handle): @staticmethod def new(): return PassManagerBuilder(_core.LLVMPassManagerBuilderCreate()) def __init__(self, ptr): - self.ptr = ptr + llvm.Handle.__init__(self, ptr) - def __del__(self): - _core.LLVMPassManagerBuilderDispose(self.ptr) + _finalizer = _core.LLVMPassManagerBuilderDispose def populate(self, pm): if isinstance(pm, FunctionPassManager): @@ -142,7 +141,7 @@ class PassManagerBuilder(object): # Pass manager #===----------------------------------------------------------------------=== -class PassManager(object): +class PassManager(llvm.Handle): @staticmethod def new(): @@ -151,8 +150,7 @@ class PassManager(object): def __init__(self, ptr): self.ptr = ptr - def __del__(self): - _core.LLVMDisposePassManager(self.ptr) + _finalizer = _core.LLVMDisposePassManager def add(self, pass_obj): '''Add a pass to the pass manager. @@ -208,9 +206,11 @@ class Pass(llvm.Ownable): '''Pass Inferface ''' def __init__(self, ptr): - llvm.Ownable.__init__(self, ptr, _core.LLVMDisposePass) + llvm.Ownable.__init__(self, ptr) self.__name = '' + _finalizer = _core.LLVMDisposePass + @staticmethod def new(name): '''Create a new pass by name. diff --git a/setup.py b/setup.py index 94c9f95..d6d6a3b 100644 --- a/setup.py +++ b/setup.py @@ -131,7 +131,7 @@ setup( maintainer = 'Continuum Analytics, Inc.', maintainer_email = 'llvmpy@continuum.io', url = 'http://www.llvmpy.org/', - packages = ['llvm', 'llvm_cbuilder', 'llpython'], + packages = ['llvm', 'llvm_cbuilder', 'llpython', 'llvm._utils'], py_modules = ['llvm.core'], license = "BSD", classifiers = [