Avoid object aliasing (with Seth Warn)

git-svn-id: http://llvm-py.googlecode.com/svn/trunk@65 8d1e9007-1d4e-0410-b67e-1979fd6579aa
This commit is contained in:
mdevan.foobar 2009-02-23 06:49:30 +00:00
commit 1b4b807884
4 changed files with 145 additions and 14 deletions

View file

@ -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

View file

@ -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

5
test/README Normal file
View file

@ -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.

91
test/objcache.py Normal file
View file

@ -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)