diff --git a/llvm/__init__.py b/llvm/__init__.py index 2533cae..e94848e 100644 --- a/llvm/__init__.py +++ b/llvm/__init__.py @@ -32,7 +32,9 @@ """ -VERSION = '0.3' +VERSION = '0.6' + +from weakref import WeakValueDictionary #===----------------------------------------------------------------------=== @@ -78,3 +80,70 @@ class Ownable(object): if not self.owner: self.del_fn(self.ptr) + +#===----------------------------------------------------------------------=== +# Dummy owner, will not delete ownee. Be careful. +#===----------------------------------------------------------------------=== + +class DummyOwner(object): + pass + + +#===----------------------------------------------------------------------=== +# A metaclass to prevent aliasing. It stores a (weak) reference to objects +# constructed based on a PyCObject. If an object is constructed based on a +# PyCObject with the same underlying pointer as a previous object, a reference +# to the previous object is returned rather than a new one. +#===----------------------------------------------------------------------=== + +class _ObjectCache(type): + """A metaclass to prevent aliasing. + + Classes using 'ObjectCache' as a metaclass must have constructors + that take a PyCObject as their first argument. When the class is + called (to create a new instance of the class), the value of the + pointer wrapped by the PyCObj is checked: + + If no previous object has been created based on the same + underlying pointer (note that different PyCObject objects can + wrap the same pointer), the object will be initialized as + usual and returned. + + If a previous has been created based on the same pointer, + then a reference to that object will be returned, and no + object initialization is performed. + """ + + __instances = WeakValueDictionary() + + def __call__(cls, ptr, *args, **kwargs): + objid = _core.PyCObjectVoidPtrToPyLong(ptr) + obj = _ObjectCache.__instances.get(objid) + if obj is None: + obj = super(_ObjectCache, cls).__call__(ptr, *args, **kwargs) + _ObjectCache.__instances[objid] = obj + return obj + + @staticmethod + def forget(obj): + objid = _core.PyCObjectVoidPtrToPyLong(obj.ptr) + if objid in _ObjectCache.__instances: + del _ObjectCache.__instances[objid] + + +#===----------------------------------------------------------------------=== +# Cacheables +#===----------------------------------------------------------------------=== + +class Cacheable(object): + """Objects that can be cached. + + Objects that wrap a PyCObject are cached to avoid "aliasing", i.e., + two Python objects each containing a PyCObject which internally points + to the same C pointer.""" + + __metaclass__ = _ObjectCache + + def forget(self): + _ObjectCache.forget(self) + diff --git a/llvm/_util.py b/llvm/_util.py index 1014910..2ffa9d9 100644 --- a/llvm/_util.py +++ b/llvm/_util.py @@ -34,7 +34,6 @@ Used only in other modules, not for public use.""" import llvm import llvm._core as _core # for PyCObjectVoidPtrToPyLong -from weakref import WeakValueDictionary #===----------------------------------------------------------------------=== @@ -42,10 +41,10 @@ from weakref import WeakValueDictionary # failures. #===----------------------------------------------------------------------=== -def check_gen(obj, type): - if not isinstance(obj, type): - type_str = type.__name__ - msg = "argument not an instance of llvm.core.%s" % type_str +def check_gen(obj, typ): + if not isinstance(obj, typ): + typ_str = typ.__name__ + msg = "argument not an instance of llvm.core.%s" % typ_str raise TypeError, msg def check_is_unowned(ownable): @@ -70,51 +69,11 @@ def unpack_gen(objlist, check_fn): # we now return a list. #===----------------------------------------------------------------------=== -def wrapiter(first, next, container, wrapper, extra=[]): -# ptr = first(container) -# while ptr: -# yield wrapper(ptr) -# ptr = next(ptr) +def wrapiter(first, next, container, wrapper): ret = [] ptr = first(container) while ptr: - ret.append(wrapper(ptr, *extra)) + ret.append(wrapper(ptr)) ptr = next(ptr) return ret - -#===----------------------------------------------------------------------=== -# A metaclass to prevent aliasing. It stores a (weak) reference to objects -# constructed based on a PyCObject. If an object is constructed based on a -# PyCObject with the same underlying pointer as a previous object, a reference -# to the previous object is returned rather than a new one. -#===----------------------------------------------------------------------=== - -class ObjectCache(type): - """A metaclass to prevent aliasing. - - Classes using 'ObjectCache' as a metaclass must have constructors - that take a PyCObject as their first argument. When the class is - called (to create a new instance of the class), the value of the - pointer wrapped by the PyCObj is checked: - - If no previous object has been created based on the same - underlying pointer (note that different PyCObject objects can - wrap the same pointer), the object will be initialized as - usual and returned. - - If a previous has been created based on the same pointer, - then a reference to that object will be returned, and no - object initialization is performed. - """ - - __instances = WeakValueDictionary() - - def __call__(cls, ptr, *args, **kwargs): - id = _core.PyCObjectVoidPtrToPyLong(ptr) - obj = ObjectCache.__instances.get(id) - if obj is None: - obj = super(ObjectCache, cls).__call__(ptr, *args, **kwargs) - ObjectCache.__instances[id] = obj - return obj - diff --git a/llvm/core.py b/llvm/core.py index 3943102..0d252b5 100644 --- a/llvm/core.py +++ b/llvm/core.py @@ -37,7 +37,6 @@ in-memory intermediate representation (IR) data structures.""" import llvm # top-level, for common stuff import llvm._core as _core # C wrappers import llvm._util as _util # utility functions -import weakref # global list of modules #===----------------------------------------------------------------------=== @@ -272,7 +271,7 @@ def _to_int(v): # Module #===----------------------------------------------------------------------=== -class Module(llvm.Ownable): +class Module(llvm.Ownable, llvm.Cacheable): """A Module instance stores all the information related to an LLVM module. Modules are the top level container of all other LLVM Intermediate @@ -287,8 +286,6 @@ class Module(llvm.Ownable): module_obj = Module.new('my_module') """ - __metaclass__ = _util.ObjectCache - @staticmethod def new(id): """Create a new Module instance. @@ -392,12 +389,18 @@ class Module(llvm.Ownable): global variables, function, etc. are matched and resolved. The `other' module is no longer valid after this method is - invoked, and will behave in undefined ways. + invoked, all refs to it should be dropped. + + In future, this API might be replaced with a full-fledged + Linker class. """ check_is_module(other) + other.forget() # remove it from object cache ret = _core.LLVMLinkModules(self.ptr, other.ptr) if isinstance(ret, str): raise llvm.LLVMException, ret + # Do not try to destroy the other module's llvm::Module*. + other._own(llvm.DummyOwner()) def add_type_name(self, name, ty): """Map a string to a type. @@ -848,9 +851,7 @@ class TypeHandle(object): # Values #===----------------------------------------------------------------------=== -class Value(object): - - __metaclass__ = _util.ObjectCache +class Value(llvm.Cacheable): def __init__(self, ptr): self.ptr = ptr @@ -1233,6 +1234,7 @@ class GlobalVariable(GlobalValue): def delete(self): self._delete() _core.LLVMDeleteGlobal(self.ptr) + self.forget() self.ptr = None def _get_initializer(self): @@ -1310,6 +1312,7 @@ class Function(GlobalValue): def delete(self): self._delete() _core.LLVMDeleteFunction(self.ptr) + self.forget() self.ptr = None @property @@ -1498,6 +1501,7 @@ class BasicBlock(Value): def delete(self): _core.LLVMDeleteBasicBlock(self.ptr) + self.forget() self.ptr = None @property diff --git a/test/objcache.py b/test/objcache.py index f2a3c88..345f873 100644 --- a/test/objcache.py +++ b/test/objcache.py @@ -8,6 +8,12 @@ def check(a, b): else: print "FAIL" +def check_isnot(a, b): + if not (a is b): + print "OK" + else: + print "FAIL" + print "Testing module aliasing ..", m1 = Module.new('a') t = Type.int() @@ -19,11 +25,18 @@ check(m1, m2) print "Testing global vairable aliasing 1 .. ", gv1 = GlobalVariable.new(m1, t, "gv") gv2 = GlobalVariable.get(m1, "gv") -check(gv2, gv2) +check(gv1, gv2) print "Testing global vairable aliasing 2 .. ", gv3 = m1.global_variables[0] -check(gv3, gv1) +check(gv1, gv3) + +print "Testing global vairable aliasing 3 .. ", +gv2 = None +gv3 = None +gv1.delete() +gv4 = GlobalVariable.new(m1, t, "gv") +check_isnot(gv1, gv4) print "Testing function aliasing 1 ..", b1 = f1.append_basic_block('entry')