Add downcasting

This commit is contained in:
Siu Kwan Lam 2013-01-25 15:35:23 -06:00
commit 3c81157ede
6 changed files with 106 additions and 32 deletions

View file

@ -28,10 +28,10 @@ class Namespace(object):
def __str__(self):
return self.name
class Type(object):
class _Type(object):
pass
class BuiltinTypes(Type):
class BuiltinTypes(_Type):
def __init__(self, name):
self.name = name
@ -47,7 +47,7 @@ Unsigned = BuiltinTypes('unsigned')
Bool = BuiltinTypes('bool')
ConstStdString = BuiltinTypes('const std::string')
class Class(Type):
class Class(_Type):
format = 'O'
def __init__(self, ns, *bases):
@ -57,9 +57,11 @@ class Class(Type):
self.methods = []
self.enums = []
self.includes = set()
self.downcastables = set()
def __call__(self, defn):
assert not self._is_defined
# process the definition in "defn"
self.name = defn.__name__
for k, v in defn.__dict__.items():
if isinstance(v, Method):
@ -79,6 +81,12 @@ class Class(Type):
else:
for i in v:
self.includes.add(i)
elif k == '_downcast_':
if isinstance(v, Class):
self.downcastables.add(v)
else:
for i in v:
self.downcastables.add(i)
return self
def compile_cpp(self, writer):
@ -106,12 +114,11 @@ class Class(Type):
bases = ', '.join(x.name for x in self.bases)
writer.println('@capsule.register_class')
with writer.block('class %(clsname)s(%(bases)s):' % locals()):
writer.println('_llvm_type_ = "%s"' % self.fullname)
for enum in self.enums:
enum.compile_py(writer)
for meth in self.methods:
meth.compile_py(writer)
if not self.enums and not self.methods:
writer.println('pass')
writer.println()
@property
@ -178,6 +185,7 @@ class Enum(object):
def compile_py(self, writer):
with writer.block('class %s:' % self.name):
writer.println('_llvm_type_ = "%s"' % self.fullname)
for v in self.value_names:
writer.println('%(v)s = "%(v)s"' % locals())
writer.println()
@ -340,7 +348,7 @@ class Constructor(StaticMethod):
ret = writer.declare(retty.fullname, stmt)
return ret
class ref(Type):
class ref(_Type):
def __init__(self, element):
assert isinstance(element, Class), type(element)
self.element = element
@ -369,7 +377,7 @@ class ref(Type):
return writer.declare(self.fullname, '*%s' % p)
class ptr(Type):
class ptr(_Type):
def __init__(self, element):
assert isinstance(element, Class)
self.element = element
@ -393,7 +401,7 @@ class ptr(Type):
return writer.pycapsule_new(val, self.element.capsule_name,
self.element.fullname)
class cast(Type):
class cast(_Type):
format = 'O'
def __init__(self, original, target):
@ -406,14 +414,14 @@ class cast(Type):
@property
def python_type(self):
if not isinstance(self.target, Type):
if not isinstance(self.target, _Type):
return self.target
else:
return self.original
@property
def binding_type(self):
if isinstance(self.target, Type):
if isinstance(self.target, _Type):
return self.target
else:
return self.original

View file

@ -19,7 +19,7 @@ def set_debug(enabled):
class WeakRef(ref):
__slots__ = 'capsule', 'dtor'
__slots__ = 'capsule', 'dtor', 'owning'
_pyclasses = {}
_addr2obj = WeakValueDictionary()
@ -33,12 +33,20 @@ def classof(cap):
return _pyclasses[cls]
def _capsule_destructor(weak):
cap = weak.capsule
addr = _capsule.getPointer(cap)
cls = _capsule.getClassName(cap)
logger.debug("destroy pointer 0x%08X to %s", addr, cls)
weak.dtor(cap)
del _owners[addr]
if weak.owning:
cap = weak.capsule
addr = _capsule.getPointer(cap)
cls = _capsule.getClassName(cap)
logger.debug("destroy pointer 0x%08X to %s", addr, cls)
weak.dtor(cap)
del _owners[addr]
def release_ownership(old):
addr = _capsule.getPointer(old)
if hasattr(old, '_delete_'):
oldweak = _owners[addr]
oldweak.owning = False # dis-own
del _owners[addr]
def wrap(cap):
'''Wrap a PyCapsule with the corresponding Wrapper class.
@ -60,13 +68,26 @@ def wrap(cap):
weak = WeakRef(obj, _capsule_destructor)
_owners[addr] = weak
weak.capsule = cap
weak.owning = True
weak.dtor = cls._delete_
else:
assert classof(obj._ptr) is classof(cap)
# Unset destructor for capsules that are repeated
_capsule.setDestructor(cap, None)
oldcls = classof(obj._ptr)
newcls = classof(cap)
if issubclass(oldcls, newcls):
# do auto downcast
pass
else:
assert oldcls is newcls
return obj
def downcast(old, new):
assert old is not new
oldcls = classof(old)
newcls = classof(new)
assert issubclass(newcls, oldcls)
release_ownership(old)
del _addr2obj[_capsule.getPointer(old)] # clear cache
return wrap(new)
def unwrap(obj):
'''Unwrap a Wrapper instance into the underlying PyCapsule.
@ -96,8 +117,3 @@ class Wrapper(object):
def _ptr(self):
return self.__ptr
def _release_ownership(self):
_capsule.setDestructor(self._ptr, None)

View file

@ -3,8 +3,13 @@ Wrapped the extra functions in _api.so
'''
import capsule
import _api
#
# Re-export the native API from the _api.extra and wrap the functions
#
def _wrapper(func):
"Wrap the re-exported functions"
def _core(*args):
unwrapped = map(capsule.unwrap, args)
ret = func(*unwrapped)
@ -12,9 +17,27 @@ def _wrapper(func):
return _core
def _init(glob):
from _api import extra
for k, v in extra.__dict__.items():
for k, v in _api.extra.__dict__.items():
glob[k] = _wrapper(v)
_init(globals())
#
# Downcasts
#
def downcast(obj, cls):
if type(obj) is cls:
return obj
fromty = obj._llvm_type_
toty = cls._llvm_type_
fname = 'downcast_%s_to_%s' % (fromty, toty)
fname = fname.replace('::', '_')
try:
caster = getattr(_api, fname)
except AttributeError:
fmt = "Downcast from %s to %s is not supported"
raise TypeError(fmt % (fromty, toty))
old = capsule.unwrap(obj)
new = caster(old)
return capsule.downcast(old, new)

View file

@ -72,6 +72,20 @@ def main():
print cls
units.append(cls)
# add extra stuffs
downcastlist = []
## add downcast
for cls in units:
if isinstance(cls, Class):
for bcls in cls.downcastables:
from_to = bcls.fullname, cls.fullname
name = 'downcast_%s_to_%s' % tuple(map(codegen.mangle, from_to))
fn = Function(namespaces[''], name, ptr(cls), ptr(bcls))
downcastlist.append((from_to, fn))
units.append(fn)
# generate cpp source
with open('%s.cpp' % outputfilename, 'w') as cppfile:
println = wrap_println_from_file(cppfile)
@ -87,6 +101,18 @@ def main():
println('#include "%s"' % inc)
println()
# generate downcast
for ((fromty, toty), fn) in downcastlist:
name = fn.name
fmt = '''
static
%(toty)s* %(name)s(%(fromty)s* arg)
{
return typecast<%(toty)s>::from(arg);
}
'''
println(fmt % locals())
# write methods and method tables
for u in units:
writer = codegen.CppCodeWriter(println)

View file

@ -37,9 +37,9 @@ PyObject* make_small_vector_from_types(PyObject* self, PyObject* args) {
}
static PyMethodDef extra_methodtable[] = {
#define method(func) { #func, (PyCFunction)func, METH_VARARGS, NULL }
method( make_raw_ostream_for_printing ),
method( make_small_vector_from_types ),
{ NULL }
#undef method
#define method(func) { #func, (PyCFunction)func, METH_VARARGS, NULL }
method( make_raw_ostream_for_printing ),
method( make_small_vector_from_types ),
#undef method
{ NULL }
};

View file

@ -1,3 +1,4 @@
from binding import *
llvm = Namespace('llvm')
default = Namespace('')