diff --git a/llvm/_util.py b/llvm/_util.py index 090ee5d..36a9b6e 100644 --- a/llvm/_util.py +++ b/llvm/_util.py @@ -33,6 +33,8 @@ Used only in other modules, not for public use.""" import llvm +import llvm._core as _core # for PyCObjectVoidPtrToPyLong +from weakref import WeakValueDictionary #===----------------------------------------------------------------------=== @@ -90,3 +92,39 @@ class dummy_owner(object): def __init__(self, ownee): ownee._own(self) + +#===----------------------------------------------------------------------=== +# 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 1eb2771..6e29093 100644 --- a/llvm/core.py +++ b/llvm/core.py @@ -878,17 +878,6 @@ def _to_int(v): else: return 0 -__all_modules = weakref.WeakValueDictionary() - -def _report_new_module(ptr, obj): - __all_modules[_core.PyCObjectVoidPtrToPyLong(ptr)] = obj - -def _module_from_ptr(ptr): - i = _core.PyCObjectVoidPtrToPyLong(ptr) - if i not in __all_modules: - raise llvm.LLVMException, "module not found in internal list" - return __all_modules[i] - #===----------------------------------------------------------------------=== # Module @@ -909,6 +898,8 @@ class Module(llvm.Ownable): module_obj = Module.new('my_module') """ + __metaclass__ = ObjectCache + @staticmethod def new(id): """Create a new Module instance. @@ -952,7 +943,6 @@ class Module(llvm.Ownable): Use the static method `Module.new' instead. """ llvm.Ownable.__init__(self, ptr, _core.LLVMDisposeModule) - _report_new_module(ptr, self) def __str__(self): """Text representation of a module. @@ -1468,6 +1458,8 @@ class TypeHandle(object): class Value(object): + __metaclass__ = ObjectCache + def __init__(self, ptr): self.ptr = ptr @@ -1803,8 +1795,7 @@ class GlobalValue(Constant): @property def module(self): - module_ptr = _core.LLVMGetGlobalParent(self.ptr) - return _module_from_ptr(module_ptr) + return Module(_core.LLVMGetGlobalParent(self.ptr)) class GlobalVariable(GlobalValue): @@ -2174,6 +2165,12 @@ class Builder(object): @property def block(self): + """Deprecated, use basic_block property instead.""" + return _make_value(_core.LLVMGetInsertBlock(self.ptr)) + + @property + def basic_block(self): + """The basic block where the builder is positioned.""" return _make_value(_core.LLVMGetInsertBlock(self.ptr)) # terminator instructions diff --git a/test/README b/test/README new file mode 100644 index 0000000..90001b1 --- /dev/null +++ b/test/README @@ -0,0 +1,5 @@ + +The files herein serve as examples or testers for various parts of llvm-py. +Everything here is checked to be working before release. All files are +self-contained. + diff --git a/test/objcache.py b/test/objcache.py new file mode 100644 index 0000000..f2a3c88 --- /dev/null +++ b/test/objcache.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python + +from llvm.core import * + +def check(a, b): + if a is b: + print "OK" + else: + print "FAIL" + +print "Testing module aliasing ..", +m1 = Module.new('a') +t = Type.int() +ft = Type.function(t, [t]) +f1 = m1.add_function(ft, "func") +m2 = f1.module +check(m1, m2) + +print "Testing global vairable aliasing 1 .. ", +gv1 = GlobalVariable.new(m1, t, "gv") +gv2 = GlobalVariable.get(m1, "gv") +check(gv2, gv2) + +print "Testing global vairable aliasing 2 .. ", +gv3 = m1.global_variables[0] +check(gv3, gv1) + +print "Testing function aliasing 1 ..", +b1 = f1.append_basic_block('entry') +f2 = b1.function +check(f1, f2) + +print "Testing function aliasing 2 ..", +f3 = m1.get_function_named("func") +check(f1, f3) + +print "Testing function aliasing 3 ..", +f4 = Function.get_or_insert(m1, ft, "func") +check(f1, f4) + +print "Testing function aliasing 4 ..", +f5 = Function.get(m1, "func") +check(f1, f5) + +print "Testing function aliasing 5 ..", +f6 = m1.get_or_insert_function(ft, "func") +check(f1, f6) + +print "Testing function aliasing 6 ..", +f7 = m1.functions[0] +check(f1, f7) + +print "Testing argument aliasing .. ", +a1 = f1.args[0] +a2 = f1.args[0] +check(a1, a2) + +print "Testing basic block aliasing 1 .. ", +b2 = f1.basic_blocks[0] +check(b1, b2) + +print "Testing basic block aliasing 2 .. ", +b3 = f1.get_entry_basic_block() +check(b1, b3) + +print "Testing basic block aliasing 3 .. ", +bldr = Builder.new(b1) +b4 = bldr.basic_block +check(b1, b4) + +print "Testing basic block aliasing 4 .. ", +i1 = bldr.ret_void() +b5 = i1.basic_block +check(b1, b5) + +print "Testing instruction aliasing 1 .. ", +i2 = b5.instructions[0] +check(i1, i2) + +# phi node +phi = bldr.phi(t) +phi.add_incoming(f1.args[0], b1) +v2 = phi.get_incoming_value(0) +b6 = phi.get_incoming_block(0) + +print "Testing PHI / basic block aliasing 5 .. ", +check(b1, b6) + +print "Testing PHI / value aliasing .. ", +check(f1.args[0], v2) +