diff --git a/newbinding/binding.py b/newbinding/binding.py index fc26d92..8c58aa9 100644 --- a/newbinding/binding.py +++ b/newbinding/binding.py @@ -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 diff --git a/newbinding/capsule.py b/newbinding/capsule.py index e4cd4fa..d999e30 100644 --- a/newbinding/capsule.py +++ b/newbinding/capsule.py @@ -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) - - - diff --git a/newbinding/extra.py b/newbinding/extra.py index 1af4e5f..a613296 100644 --- a/newbinding/extra.py +++ b/newbinding/extra.py @@ -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) \ No newline at end of file diff --git a/newbinding/gen.py b/newbinding/gen.py index 4d8709e..f1af18e 100644 --- a/newbinding/gen.py +++ b/newbinding/gen.py @@ -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) diff --git a/newbinding/include/llvm_binding/extra.h b/newbinding/include/llvm_binding/extra.h index 417c0f8..94887b3 100644 --- a/newbinding/include/llvm_binding/extra.h +++ b/newbinding/include/llvm_binding/extra.h @@ -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 } }; diff --git a/newbinding/src/namespace.py b/newbinding/src/namespace.py index c50256d..3a081af 100644 --- a/newbinding/src/namespace.py +++ b/newbinding/src/namespace.py @@ -1,3 +1,4 @@ from binding import * llvm = Namespace('llvm') +default = Namespace('')