Move capsule code into C++
This commit is contained in:
parent
b9752e1e98
commit
078f1fe5c1
3 changed files with 708 additions and 169 deletions
|
|
@ -48,7 +48,6 @@ def run(verbosity=1):
|
||||||
testresult = runner.run(suite)
|
testresult = runner.run(suite)
|
||||||
print(hp.heap())
|
print(hp.heap())
|
||||||
|
|
||||||
|
|
||||||
if testresult:
|
if testresult:
|
||||||
# Run isolated tests
|
# Run isolated tests
|
||||||
print("run isolated tests".center(80, '-'))
|
print("run isolated tests".center(80, '-'))
|
||||||
|
|
@ -60,7 +59,7 @@ def run(verbosity=1):
|
||||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
|
p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.STDOUT)
|
stderr=subprocess.STDOUT)
|
||||||
for line in p.stdout:
|
for line in p.stdout:
|
||||||
print(line, end='')
|
print(line.decode('utf8'), end='')
|
||||||
p.wait()
|
p.wait()
|
||||||
if p.returncode:
|
if p.returncode:
|
||||||
raise Exception("%s returned: %d" % p.returncode)
|
raise Exception("%s returned: %d" % p.returncode)
|
||||||
|
|
|
||||||
|
|
@ -1,55 +1,95 @@
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
|
#include <structmember.h>
|
||||||
#include <python3adapt.h>
|
#include <python3adapt.h>
|
||||||
#include <capsulethunk.h>
|
#include <capsulethunk.h>
|
||||||
#include <llvm_binding/capsule_context.h>
|
#include <llvm_binding/capsule_context.h>
|
||||||
|
#include <llvm_binding/auto_pyobject.h>
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
static PyObject* TheAPIModule = NULL;
|
||||||
|
static PyObject* TheCapsuleModule = NULL;
|
||||||
|
static PyObject* TheCapsuleClass = NULL;
|
||||||
|
static PyObject* TheWrapperClass = NULL;
|
||||||
|
static PyObject* TheCache = NULL;
|
||||||
|
static PyObject* TheAddrDtorDict = NULL;
|
||||||
|
static PyObject* TheClassesDict = NULL;
|
||||||
|
static PyObject* TheAddrRefCt = NULL;
|
||||||
|
static PyObject* ConstantOne = NULL;
|
||||||
|
static PyObject* TheDowncastModule = NULL;
|
||||||
|
|
||||||
static
|
static
|
||||||
PyObject* getName(PyObject* self, PyObject* args) {
|
PyObject* GetAPIModule(){
|
||||||
PyObject* obj;
|
if (NULL == TheAPIModule)
|
||||||
if (!PyArg_ParseTuple(args, "O", &obj)){
|
TheAPIModule = PyImport_ImportModule("llvmpy._api");
|
||||||
return NULL;
|
return TheAPIModule;
|
||||||
}
|
|
||||||
const char* name = PyCapsule_GetName(obj);
|
|
||||||
if (!name) return NULL;
|
|
||||||
|
|
||||||
return PyString_FromString(name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
PyObject* getPointer(PyObject* self, PyObject* args) {
|
PyObject* GetCapsuleModule(){
|
||||||
PyObject* obj;
|
if (NULL == TheCapsuleModule)
|
||||||
if (!PyArg_ParseTuple(args, "O", &obj)){
|
TheCapsuleModule = PyImport_ImportModule("llvmpy.capsule");
|
||||||
return NULL;
|
return TheCapsuleModule;
|
||||||
}
|
|
||||||
void* pointer = PyCapsule_GetPointer(obj, PyCapsule_GetName(obj));
|
|
||||||
if (!pointer) return NULL;
|
|
||||||
|
|
||||||
return PyLong_FromVoidPtr(pointer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
PyObject* check(PyObject* self, PyObject* args) {
|
PyObject* GetCapsuleClass() {
|
||||||
PyObject* obj;
|
if (NULL == TheCapsuleClass)
|
||||||
if (!PyArg_ParseTuple(args, "O", &obj)){
|
TheCapsuleClass = PyObject_GetAttrString(GetCapsuleModule(),
|
||||||
return NULL;
|
"Capsule");
|
||||||
}
|
return TheCapsuleClass;
|
||||||
if (PyCapsule_CheckExact(obj)) {
|
|
||||||
Py_RETURN_TRUE;
|
|
||||||
} else {
|
|
||||||
Py_RETURN_FALSE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------
|
static
|
||||||
// PyCapsule Context
|
PyObject* GetWrapperClass() {
|
||||||
// ------------------
|
if (NULL == TheWrapperClass)
|
||||||
|
TheWrapperClass = PyObject_GetAttrString(GetCapsuleModule(),
|
||||||
|
"Wrapper");
|
||||||
|
return TheWrapperClass;
|
||||||
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
CapsuleContext* getContext(PyObject* self, PyObject* args) {
|
PyObject* GetCache() {
|
||||||
PyObject* obj;
|
if (NULL == TheCache)
|
||||||
if (!PyArg_ParseTuple(args, "O", &obj)) {
|
TheCache = PyObject_GetAttrString(GetCapsuleModule(), "_cache");
|
||||||
return NULL;
|
return TheCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
PyObject* GetAddrDtorDict() {
|
||||||
|
if (NULL == TheAddrDtorDict)
|
||||||
|
TheAddrDtorDict = PyObject_GetAttrString(GetCapsuleModule(),
|
||||||
|
"_addr2dtor");
|
||||||
|
return TheAddrDtorDict;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
PyObject* GetClassesDict() {
|
||||||
|
if (NULL == TheClassesDict)
|
||||||
|
TheClassesDict = PyObject_GetAttrString(GetCapsuleModule(),
|
||||||
|
"_pyclasses");
|
||||||
|
return TheClassesDict;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
PyObject* GetAddrRefCt() {
|
||||||
|
if (NULL == TheAddrRefCt)
|
||||||
|
TheAddrRefCt = PyObject_GetAttrString(GetCapsuleModule(),
|
||||||
|
"_addr2refct");
|
||||||
|
return TheAddrRefCt;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
PyObject* GetDowncastModule() {
|
||||||
|
if (NULL == TheDowncastModule)
|
||||||
|
TheDowncastModule = PyObject_GetAttrString(GetAPIModule(),
|
||||||
|
"downcast");
|
||||||
|
return TheDowncastModule;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
CapsuleContext* GetContext(PyObject *obj) {
|
||||||
void* context = PyCapsule_GetContext(obj);
|
void* context = PyCapsule_GetContext(obj);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
PyErr_SetString(PyExc_TypeError, "PyCapsule has no context.");
|
PyErr_SetString(PyExc_TypeError, "PyCapsule has no context.");
|
||||||
|
|
@ -59,9 +99,8 @@ CapsuleContext* getContext(PyObject* self, PyObject* args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
PyObject* getClassName(PyObject* self, PyObject* args) {
|
PyObject* GetClassName(PyObject* obj) {
|
||||||
CapsuleContext* context = getContext(self, args);
|
CapsuleContext* context = GetContext(obj);
|
||||||
//Assert(context->_magic == 0xdead);
|
|
||||||
if (!context) {
|
if (!context) {
|
||||||
return NULL;
|
return NULL;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -69,6 +108,474 @@ PyObject* getClassName(PyObject* self, PyObject* args) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
PyObject* getClassName(PyObject* self, PyObject* args) {
|
||||||
|
PyObject* obj;
|
||||||
|
if (!PyArg_ParseTuple(args, "O", &obj)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return GetClassName(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
PyObject* GetName(PyObject* obj) {
|
||||||
|
const char* name = PyCapsule_GetName(obj);
|
||||||
|
if (!name) return NULL;
|
||||||
|
return PyString_FromString(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
PyObject* getName(PyObject* self, PyObject* args) {
|
||||||
|
PyObject* obj;
|
||||||
|
if (!PyArg_ParseTuple(args, "O", &obj)){
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return GetName(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
PyObject* GetPointer(PyObject* obj){
|
||||||
|
void *pointer = PyCapsule_GetPointer(obj, PyCapsule_GetName(obj));
|
||||||
|
if (!pointer) return NULL;
|
||||||
|
return PyLong_FromVoidPtr(pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
PyObject* getPointer(PyObject* self, PyObject* args) {
|
||||||
|
PyObject* obj;
|
||||||
|
if (!PyArg_ParseTuple(args, "O", &obj)){
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return GetPointer(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
bool Check(PyObject* obj){
|
||||||
|
return PyCapsule_CheckExact(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
PyObject* check(PyObject* self, PyObject* args) {
|
||||||
|
PyObject* obj;
|
||||||
|
if (!PyArg_ParseTuple(args, "O", &obj)){
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (Check(obj)) {
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
} else {
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject* Unwrap(PyObject* obj) {
|
||||||
|
if (PyObject_IsInstance(obj, GetWrapperClass())) {
|
||||||
|
return PyObject_GetAttrString(obj, "_ptr");
|
||||||
|
} else {
|
||||||
|
Py_INCREF(obj);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Unwrap a Wrapper instance into the underlying PyCapsule.
|
||||||
|
If `obj` is not a Wrapper instance, returns `obj`.
|
||||||
|
*/
|
||||||
|
static PyObject* unwrap(PyObject* self, PyObject* args) {
|
||||||
|
PyObject *obj;
|
||||||
|
if (!PyArg_ParseTuple(args, "O", &obj)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return Unwrap(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HasOwnership(PyObject* obj) {
|
||||||
|
PyObject* addr = GetPointer(obj);
|
||||||
|
PyObject* name = GetName(obj);
|
||||||
|
auto_pyobject nameaddr = PyTuple_Pack(2, name, addr);
|
||||||
|
PyObject* dtor = PyDict_GetItem(GetAddrDtorDict(), *nameaddr);
|
||||||
|
if (!dtor || dtor == Py_None) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject* has_ownership(PyObject* self, PyObject* args) {
|
||||||
|
PyObject *obj;
|
||||||
|
if (!PyArg_ParseTuple(args, "O", &obj)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (HasOwnership(obj)) {
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
} else {
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void NormalizeString(std::ostream &os, const char* str) {
|
||||||
|
for(; *str; ++str){
|
||||||
|
if (*str == ':') {
|
||||||
|
os << '_';
|
||||||
|
if(*(str + 1) == ':') { ++str; }
|
||||||
|
} else {
|
||||||
|
os << *str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
PyObject* WrapCore(PyObject *oldCap, bool owned) {
|
||||||
|
auto_pyobject cap = PyObject_CallFunctionObjArgs(GetCapsuleClass(), oldCap,
|
||||||
|
NULL);
|
||||||
|
auto_pyobject cls = PyObject_CallMethod(*cap, "get_class", "");
|
||||||
|
auto_pyobject addr = GetPointer(oldCap);
|
||||||
|
auto_pyobject name = GetName(oldCap);
|
||||||
|
|
||||||
|
// look up cached object
|
||||||
|
auto_pyobject cache_cls = PyObject_GetItem(GetCache(), *cls);
|
||||||
|
Assert(*cache_cls);
|
||||||
|
int addr_in_cache = PyMapping_HasKey(*cache_cls, *addr);
|
||||||
|
|
||||||
|
PyObject* obj = NULL;
|
||||||
|
if (addr_in_cache) {
|
||||||
|
obj = PyObject_GetItem(*cache_cls, *addr);
|
||||||
|
} else {
|
||||||
|
if (!owned) {
|
||||||
|
auto_pyobject hasDtor = PyObject_CallMethod(*cls, "_has_dtor", "");
|
||||||
|
if (PyObject_IsTrue(*hasDtor)) {
|
||||||
|
auto_pyobject key = PyTuple_Pack(2, *name, *addr);
|
||||||
|
auto_pyobject val = PyObject_GetAttrString(*cls, "_delete_");
|
||||||
|
|
||||||
|
int ok = PyDict_SetItem(GetAddrDtorDict(), *key, *val);
|
||||||
|
Assert(ok != -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
obj = PyObject_CallMethod(*cap, "instantiate", "");
|
||||||
|
int ok = PyObject_SetItem(*cache_cls, *addr, obj);
|
||||||
|
Assert(ok != -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert(obj);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
PyObject* Wrap(PyObject* cap, bool owned){
|
||||||
|
if (!Check(cap)) {
|
||||||
|
if (PyList_Check(cap)) {
|
||||||
|
const int N = PyList_Size(cap);
|
||||||
|
PyObject* result = PyList_New(N);
|
||||||
|
|
||||||
|
for (int i = 0; i < N; ++i){
|
||||||
|
PyObject* item = PyList_GetItem(cap, i);
|
||||||
|
if (!item)
|
||||||
|
return NULL;
|
||||||
|
PyObject* out = Wrap(item, false);
|
||||||
|
if (!out) return NULL;
|
||||||
|
if (-1 == PyList_SetItem(result, i, out))
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
Py_INCREF(cap);
|
||||||
|
return cap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return WrapCore(cap, owned);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static
|
||||||
|
PyObject* wrap(PyObject* self, PyObject* args) {
|
||||||
|
PyObject* obj;
|
||||||
|
PyObject* owned = NULL;
|
||||||
|
if (!PyArg_ParseTuple(args, "O|O", &obj, &owned)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
bool ownedFlag = false;
|
||||||
|
if (owned) {
|
||||||
|
ownedFlag = PyObject_IsTrue(owned);
|
||||||
|
}
|
||||||
|
return Wrap(obj, ownedFlag);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
PyObject* downcast(PyObject* self, PyObject* args) {
|
||||||
|
PyObject *obj, *cls;
|
||||||
|
if (!PyArg_ParseTuple(args, "OO", &obj, &cls)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto_pyobject objType = PyObject_Type(obj);
|
||||||
|
|
||||||
|
if (*objType == cls) {
|
||||||
|
Py_INCREF(obj);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* apiModule = GetAPIModule();
|
||||||
|
|
||||||
|
auto_pyobject fromTy = PyObject_GetAttrString(obj, "_llvm_type_");
|
||||||
|
auto_pyobject toTy = PyObject_GetAttrString(cls, "_llvm_type_");
|
||||||
|
|
||||||
|
std::ostringstream oss;
|
||||||
|
|
||||||
|
auto_pyobject fromTyStr = PyObject_Str(*fromTy);
|
||||||
|
auto_pyobject toTyStr = PyObject_Str(*toTy);
|
||||||
|
|
||||||
|
const char * fromCS = PyString_AsString(*fromTyStr);
|
||||||
|
const char * toCS = PyString_AsString(*toTyStr);
|
||||||
|
|
||||||
|
oss << "downcast_";
|
||||||
|
NormalizeString(oss, fromCS);
|
||||||
|
oss << "_to_";
|
||||||
|
NormalizeString(oss, toCS);
|
||||||
|
std::string fname = oss.str();
|
||||||
|
|
||||||
|
auto_pyobject caster = PyObject_GetAttrString(GetDowncastModule(),
|
||||||
|
fname.c_str());
|
||||||
|
|
||||||
|
if (!caster) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Downcast from " << fromCS << " to " << toCS;
|
||||||
|
std::string errmsg = oss.str();
|
||||||
|
PyErr_SetString(PyExc_TypeError, errmsg.c_str());
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto_pyobject oldObj = Unwrap(obj);
|
||||||
|
auto_pyobject newObj = PyObject_CallFunctionObjArgs(*caster, *oldObj,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
bool used_to_own = HasOwnership(*oldObj);
|
||||||
|
|
||||||
|
PyObject *result = Wrap(*newObj, !used_to_own);
|
||||||
|
|
||||||
|
int status = PyObject_Not(result);
|
||||||
|
switch(status) {
|
||||||
|
case 0:
|
||||||
|
return result;
|
||||||
|
case 1:
|
||||||
|
default:
|
||||||
|
PyErr_SetString(PyExc_ValueError, "Downcast failed");
|
||||||
|
Py_XDECREF(result);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
struct CapsuleObject {
|
||||||
|
PyObject_HEAD;
|
||||||
|
PyObject *capsule;
|
||||||
|
};
|
||||||
|
|
||||||
|
static
|
||||||
|
void Capsule_dealloc(CapsuleObject* self) {
|
||||||
|
Py_XDECREF(self->capsule);
|
||||||
|
Py_TYPE(self)->tp_free((PyObject*)self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int Capsule_init(CapsuleObject *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
PyObject *cap;
|
||||||
|
if (!PyArg_ParseTuple(args, "O", &cap)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Check(cap)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "Expected PyCapsule object");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_INCREF(cap);
|
||||||
|
self->capsule = cap;
|
||||||
|
|
||||||
|
PyObject* addr2refct = GetAddrRefCt();
|
||||||
|
|
||||||
|
auto_pyobject ptr = GetPointer(self->capsule);
|
||||||
|
auto_pyobject refct = PyObject_GetItem(addr2refct, *ptr);
|
||||||
|
auto_pyobject inc = PyNumber_InPlaceAdd(*refct, ConstantOne);
|
||||||
|
return PyObject_SetItem(addr2refct, *ptr, *inc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
PyObject* Capsule_getclassname(CapsuleObject* self, void *closure) {
|
||||||
|
return GetClassName(self->capsule);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
PyObject* Capsule_getname(CapsuleObject* self, void *closure) {
|
||||||
|
return GetName(self->capsule);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
PyObject* Capsule_getpointer(CapsuleObject* self, void *closure) {
|
||||||
|
return GetPointer(self->capsule);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static
|
||||||
|
PyObject* Capsule_GetClass(CapsuleObject *self){
|
||||||
|
PyObject *pycls = GetClassesDict();
|
||||||
|
auto_pyobject key = GetClassName(self->capsule);
|
||||||
|
return PyDict_GetItem(pycls, *key); // borrowed reference
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
PyObject* Capsule_get_class(CapsuleObject* self, PyObject* args) {
|
||||||
|
PyObject* out = Capsule_GetClass(self);
|
||||||
|
Py_XINCREF(out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
PyObject* Capsule_instantiate(CapsuleObject* self, PyObject* args) {
|
||||||
|
return PyObject_CallFunctionObjArgs(Capsule_GetClass(self), self, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rotate Right
|
||||||
|
static unsigned long RotR(unsigned long hash, int offset){
|
||||||
|
if (offset == 0) return hash;
|
||||||
|
|
||||||
|
unsigned long out = hash << (sizeof(hash) * 8 - offset);
|
||||||
|
out |= hash >> offset;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is called everytime an object is returned from LLVM.
|
||||||
|
It derserves to be optimized to reduce unnecessary Python object allocation.
|
||||||
|
The following implements a simple hash function that uses XOR and
|
||||||
|
right-rotation.
|
||||||
|
*/
|
||||||
|
static
|
||||||
|
long Capsule_hash(CapsuleObject *self) {
|
||||||
|
const char* name = PyCapsule_GetName(self->capsule);
|
||||||
|
void *pointer = PyCapsule_GetPointer(self->capsule, name);
|
||||||
|
|
||||||
|
unsigned long hash = 0xabcd1234 ^ (unsigned long)pointer;
|
||||||
|
|
||||||
|
// The first loop accounts for the different LLVM class name and the
|
||||||
|
// length of the name.
|
||||||
|
for(const char* p = name; *p != '\0'; ++p) {
|
||||||
|
hash ^= *p;
|
||||||
|
hash = RotR(hash, 11);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The second loop accounts for the pointer identity.
|
||||||
|
for(int i = 0; i <sizeof(pointer); ++i) {
|
||||||
|
hash ^= ((unsigned char*)&pointer)[i];
|
||||||
|
hash = RotR(hash, 11);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
bool Capsule_eq(PyObject *self, PyObject *other) {
|
||||||
|
if (PyObject_Type(self) == PyObject_Type(other)) {
|
||||||
|
CapsuleObject *a = (CapsuleObject*)self;
|
||||||
|
CapsuleObject *b = (CapsuleObject*)other;
|
||||||
|
|
||||||
|
void* pa = PyCapsule_GetPointer(a->capsule,
|
||||||
|
PyCapsule_GetName(a->capsule));
|
||||||
|
void* pb = PyCapsule_GetPointer(b->capsule,
|
||||||
|
PyCapsule_GetName(b->capsule));
|
||||||
|
return pa == pb;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
PyObject* Capsule_richcmp(PyObject *a, PyObject *b, int op) {
|
||||||
|
bool ret = Capsule_eq(a, b);
|
||||||
|
switch(op) {
|
||||||
|
case Py_EQ: break;
|
||||||
|
case Py_NE: ret = !ret; break;
|
||||||
|
default:
|
||||||
|
return Py_NotImplemented;
|
||||||
|
}
|
||||||
|
if (ret) {
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
} else {
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyMemberDef Capsule_members[] = {
|
||||||
|
{"capsule", T_OBJECT_EX, offsetof(CapsuleObject, capsule), READONLY, "capsule"},
|
||||||
|
{ NULL },
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyMethodDef Capsule_methods[] = {
|
||||||
|
{ "get_class", (PyCFunction)Capsule_get_class, METH_NOARGS,
|
||||||
|
"Get Capsule class"},
|
||||||
|
{ "instantiate", (PyCFunction)Capsule_instantiate, METH_NOARGS,
|
||||||
|
"create a new instance"},
|
||||||
|
{ NULL },
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyGetSetDef Capsule_getseters[] = {
|
||||||
|
{"classname", (getter)Capsule_getclassname, NULL, "class name", NULL},
|
||||||
|
{"name", (getter)Capsule_getname, NULL, "name", NULL},
|
||||||
|
{"pointer", (getter)Capsule_getpointer, NULL, "pointer", NULL},
|
||||||
|
{ NULL },
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyTypeObject CapsuleType = {
|
||||||
|
PyObject_HEAD_INIT(NULL)
|
||||||
|
#if (PY_MAJOR_VERSION < 3)
|
||||||
|
0, /*ob_size*/
|
||||||
|
#endif
|
||||||
|
"_capsule.Capsule", /*tp_name*/
|
||||||
|
sizeof(CapsuleObject), /*tp_basicsize*/
|
||||||
|
0, /*tp_itemsize*/
|
||||||
|
(destructor)Capsule_dealloc, /*tp_dealloc*/
|
||||||
|
0, /*tp_print*/
|
||||||
|
0, /*tp_getattr*/
|
||||||
|
0, /*tp_setattr*/
|
||||||
|
0, /*tp_compare*/
|
||||||
|
0, /*tp_repr*/
|
||||||
|
0, /*tp_as_number*/
|
||||||
|
0, /*tp_as_sequence*/
|
||||||
|
0, /*tp_as_mapping*/
|
||||||
|
(hashfunc)Capsule_hash, /*tp_hash */
|
||||||
|
0, /*tp_call*/
|
||||||
|
0, /*tp_str*/
|
||||||
|
0, /*tp_getattro*/
|
||||||
|
0, /*tp_setattro*/
|
||||||
|
0, /*tp_as_buffer*/
|
||||||
|
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
||||||
|
"Capsule object", /* tp_doc */
|
||||||
|
0, /* tp_traverse */
|
||||||
|
0, /* tp_clear */
|
||||||
|
(richcmpfunc)Capsule_richcmp, /*tp_richcompare */
|
||||||
|
0, /* tp_weaklistoffset */
|
||||||
|
0, /* tp_iter */
|
||||||
|
0, /* tp_iternext */
|
||||||
|
Capsule_methods, /* tp_methods */
|
||||||
|
Capsule_members, /* tp_members */
|
||||||
|
Capsule_getseters, /* tp_getset */
|
||||||
|
0, /* tp_base */
|
||||||
|
0, /* tp_dict */
|
||||||
|
0, /* tp_descr_get */
|
||||||
|
0, /* tp_descr_set */
|
||||||
|
0, /* tp_dictoffset */
|
||||||
|
(initproc)Capsule_init, /* tp_init */
|
||||||
|
0, /* tp_alloc */
|
||||||
|
0, /* tp_new */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
static PyMethodDef core_methods[] = {
|
static PyMethodDef core_methods[] = {
|
||||||
#define declmethod(func) { #func , ( PyCFunction )func , METH_VARARGS , NULL }
|
#define declmethod(func) { #func , ( PyCFunction )func , METH_VARARGS , NULL }
|
||||||
|
|
@ -76,6 +583,10 @@ static PyMethodDef core_methods[] = {
|
||||||
declmethod(getPointer),
|
declmethod(getPointer),
|
||||||
declmethod(check),
|
declmethod(check),
|
||||||
declmethod(getClassName),
|
declmethod(getClassName),
|
||||||
|
declmethod(unwrap),
|
||||||
|
declmethod(wrap),
|
||||||
|
declmethod(has_ownership),
|
||||||
|
declmethod(downcast),
|
||||||
{ NULL },
|
{ NULL },
|
||||||
#undef declmethod
|
#undef declmethod
|
||||||
};
|
};
|
||||||
|
|
@ -106,10 +617,21 @@ extern "C" {
|
||||||
#else
|
#else
|
||||||
PyObject *module = Py_InitModule("_capsule", core_methods);
|
PyObject *module = Py_InitModule("_capsule", core_methods);
|
||||||
#endif
|
#endif
|
||||||
if (module == NULL)
|
if (module == NULL){
|
||||||
INITERROR;
|
INITERROR;
|
||||||
#if PY_MAJOR_VERSION >= 3
|
}
|
||||||
|
|
||||||
|
if (module) {
|
||||||
|
CapsuleType.tp_new = PyType_GenericNew;
|
||||||
|
if (PyType_Ready(&CapsuleType) < 0) {
|
||||||
|
INITERROR;
|
||||||
|
}
|
||||||
|
Py_INCREF(&CapsuleType);
|
||||||
|
PyModule_AddObject(module, "Capsule", (PyObject*)(&CapsuleType));
|
||||||
|
|
||||||
|
ConstantOne = PyInt_FromLong(1);
|
||||||
|
}
|
||||||
|
#if PY_MAJOR_VERSION >= 3
|
||||||
return module;
|
return module;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,27 @@
|
||||||
from weakref import WeakKeyDictionary, WeakValueDictionary, ref
|
from weakref import WeakValueDictionary
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
import logging
|
import logging
|
||||||
|
from llvmpy._capsule import (unwrap, has_ownership, downcast, wrap,
|
||||||
|
getClassName, getName, getPointer, Capsule)
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
NO_DEBUG = False
|
NO_DEBUG = False
|
||||||
|
|
||||||
|
|
||||||
def silent_logger():
|
def silent_logger():
|
||||||
'''
|
'''
|
||||||
Silent logger for unless we have a error message.
|
Silent logger for unless we have a error message.
|
||||||
'''
|
'''
|
||||||
|
global NO_DEBUG
|
||||||
logger.setLevel(logging.ERROR)
|
logger.setLevel(logging.ERROR)
|
||||||
NO_DEBUG = True
|
NO_DEBUG = True
|
||||||
|
|
||||||
# comment out the line below to re-enable logging at DEBUG level.
|
# comment out the line below to re-enable logging at DEBUG level.
|
||||||
silent_logger()
|
silent_logger()
|
||||||
|
|
||||||
|
|
||||||
def set_debug(enabled):
|
def set_debug(enabled):
|
||||||
'''
|
'''
|
||||||
Side-effect: configure logger with it is not configured.
|
Side-effect: configure logger with it is not configured.
|
||||||
|
|
@ -28,80 +36,63 @@ def set_debug(enabled):
|
||||||
else:
|
else:
|
||||||
logger.setLevel(logging.WARNING)
|
logger.setLevel(logging.WARNING)
|
||||||
|
|
||||||
def _capsule_weakref_dtor(item):
|
|
||||||
addr = item.pointer
|
|
||||||
name = item.name
|
|
||||||
_addr2refct[addr] -= 1
|
|
||||||
refct = _addr2refct[addr]
|
|
||||||
assert refct >= 0, "RefCt drop below 0"
|
|
||||||
if refct == 0:
|
|
||||||
dtor = _addr2dtor.pop((name, addr), None)
|
|
||||||
if dtor is not None:
|
|
||||||
if not NO_DEBUG:
|
|
||||||
# Some globals in logger could be removed by python GC
|
|
||||||
# at interpreter teardown.
|
|
||||||
# That can cause exception raised and ignored message.
|
|
||||||
logger.debug('Destroy %s %s', name, hex(addr))
|
|
||||||
dtor(item.capsule)
|
|
||||||
|
|
||||||
class Capsule(object):
|
#class Capsule(object):
|
||||||
"Wraps PyCapsule so that we can build weakref of it."
|
# "Wraps PyCapsule so that we can build weakref of it."
|
||||||
__slots__ = 'pointer', 'capsule', 'name', '__weakref__'
|
# from ._capsule import check, getClassName, getName, getPointer
|
||||||
|
# __slots__ = "capsule"
|
||||||
|
#
|
||||||
|
# def __init__(self, capsule):
|
||||||
|
# assert Capsule.valid(capsule)
|
||||||
|
# self.capsule = capsule
|
||||||
|
#
|
||||||
|
# #weak = WeakRef(self, _capsule_weakref_dtor)
|
||||||
|
# #weak.pointer = self.pointer
|
||||||
|
# #weak.capsule = capsule
|
||||||
|
# #weak.name = self.name
|
||||||
|
# #_capsule2weak[self] = weak
|
||||||
|
# _addr2refct[self.pointer] += 1
|
||||||
|
#
|
||||||
|
# @property
|
||||||
|
# def classname(self):
|
||||||
|
# return self.getClassName(self.capsule)
|
||||||
|
#
|
||||||
|
# @property
|
||||||
|
# def name(self):
|
||||||
|
# return self.getName(self.capsule)
|
||||||
|
#
|
||||||
|
# @property
|
||||||
|
# def pointer(self):
|
||||||
|
# return self.getPointer(self.capsule)
|
||||||
|
#
|
||||||
|
# @staticmethod
|
||||||
|
# def valid(capsule):
|
||||||
|
# return Capsule.check(capsule)
|
||||||
|
#
|
||||||
|
# def get_class(self):
|
||||||
|
# return _pyclasses[self.classname]
|
||||||
|
#
|
||||||
|
# def instantiate(self):
|
||||||
|
# cls = self.get_class()
|
||||||
|
# return cls(self)
|
||||||
|
#
|
||||||
|
# def __eq__(self, other):
|
||||||
|
# if isinstance(other, Capsule) and self.pointer == other.pointer:
|
||||||
|
# assert self.name == other.name
|
||||||
|
# return True
|
||||||
|
# else:
|
||||||
|
# return False
|
||||||
|
#
|
||||||
|
# def __hash__(self):
|
||||||
|
# return hash((self.pointer, self.name))
|
||||||
|
#
|
||||||
|
# def __ne__(self, other):
|
||||||
|
# return not (self == other)
|
||||||
|
|
||||||
from ._capsule import check, getClassName, getName, getPointer
|
|
||||||
|
|
||||||
def __init__(self, capsule):
|
|
||||||
assert Capsule.valid(capsule)
|
|
||||||
self.capsule = capsule
|
|
||||||
|
|
||||||
weak = WeakRef(self, _capsule_weakref_dtor)
|
|
||||||
weak.pointer = self.pointer
|
|
||||||
weak.capsule = capsule
|
|
||||||
weak.name = self.name
|
|
||||||
_capsule2weak[self] = weak
|
|
||||||
_addr2refct[self.pointer] += 1
|
|
||||||
|
|
||||||
@property
|
|
||||||
def classname(self):
|
|
||||||
return self.getClassName(self.capsule)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
return self.getName(self.capsule)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def pointer(self):
|
|
||||||
return self.getPointer(self.capsule)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def valid(capsule):
|
|
||||||
return Capsule.check(capsule)
|
|
||||||
|
|
||||||
def get_class(self):
|
|
||||||
return _pyclasses[self.classname]
|
|
||||||
|
|
||||||
def instantiate(self):
|
|
||||||
cls = self.get_class()
|
|
||||||
return cls(self)
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
if isinstance(other, Capsule) and self.pointer == other.pointer:
|
|
||||||
assert self.name == other.name
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def __hash__(self):
|
|
||||||
return hash((self.pointer, self.name))
|
|
||||||
|
|
||||||
def __ne__(self, other):
|
|
||||||
return not (self == other)
|
|
||||||
|
|
||||||
class WeakRef(ref):
|
|
||||||
__slots__ = 'pointer', 'name', 'capsule'
|
|
||||||
|
|
||||||
_addr2refct = defaultdict(lambda: 0)
|
_addr2refct = defaultdict(lambda: 0)
|
||||||
_capsule2weak = WeakKeyDictionary()
|
#_capsule2weak = WeakKeyDictionary()
|
||||||
_addr2dtor = {}
|
_addr2dtor = {}
|
||||||
_pyclasses = {}
|
_pyclasses = {}
|
||||||
|
|
||||||
|
|
@ -109,12 +100,13 @@ _pyclasses = {}
|
||||||
# NOTE: The same 'addr' may appear in multiple class bins.
|
# NOTE: The same 'addr' may appear in multiple class bins.
|
||||||
_cache = defaultdict(WeakValueDictionary)
|
_cache = defaultdict(WeakValueDictionary)
|
||||||
|
|
||||||
|
|
||||||
def release_ownership(old):
|
def release_ownership(old):
|
||||||
logger.debug('Release %s', old)
|
logger.debug('Release %s', old)
|
||||||
addr = Capsule.getPointer(old)
|
addr = getPointer(old)
|
||||||
name = Capsule.getName(old)
|
name = getName(old)
|
||||||
if _addr2dtor.get((name, addr)) is None:
|
if _addr2dtor.get((name, addr)) is None:
|
||||||
clsname = Capsule.getClassName(old)
|
clsname = getClassName(old)
|
||||||
if not _pyclasses[clsname]._has_dtor():
|
if not _pyclasses[clsname]._has_dtor():
|
||||||
return
|
return
|
||||||
# Guard duplicated release
|
# Guard duplicated release
|
||||||
|
|
@ -130,58 +122,83 @@ def obtain_ownership(cap):
|
||||||
assert _addr2dtor[(name, addr)] is None
|
assert _addr2dtor[(name, addr)] is None
|
||||||
_addr2dtor[(name, addr)] = cls._delete_
|
_addr2dtor[(name, addr)] = cls._delete_
|
||||||
|
|
||||||
def has_ownership(cap):
|
|
||||||
addr = Capsule.getPointer(cap)
|
|
||||||
name = Capsule.getName(cap)
|
|
||||||
return _addr2dtor.get((name, addr)) is not None
|
|
||||||
|
|
||||||
def wrap(cap, owned=False):
|
#def has_ownership(cap):
|
||||||
'''Wrap a PyCapsule with the corresponding Wrapper class.
|
# addr = Capsule.getPointer(cap)
|
||||||
If `cap` is not a PyCapsule, returns `cap`
|
# name = Capsule.getName(cap)
|
||||||
'''
|
# return _addr2dtor.get((name, addr)) is not None
|
||||||
if not Capsule.valid(cap):
|
|
||||||
if isinstance(cap, list):
|
|
||||||
return list(map(wrap, cap))
|
|
||||||
return cap # bypass if cap is not a PyCapsule and not a list
|
|
||||||
|
|
||||||
cap = Capsule(cap)
|
|
||||||
cls = cap.get_class()
|
|
||||||
addr = cap.pointer
|
|
||||||
name = cap.name
|
|
||||||
# lookup cached object
|
|
||||||
if cls in _cache and addr in _cache[cls]:
|
|
||||||
obj = _cache[cls][addr]
|
|
||||||
else:
|
|
||||||
if not owned and cls._has_dtor():
|
|
||||||
_addr2dtor[(name, addr)] = cls._delete_
|
|
||||||
obj = cap.instantiate()
|
|
||||||
_cache[cls][addr] = obj # cache it
|
|
||||||
return obj
|
|
||||||
|
|
||||||
def unwrap(obj):
|
#def wrap(cap, owned=False):
|
||||||
|
# '''Wrap a PyCapsule with the corresponding Wrapper class.
|
||||||
|
# If `cap` is not a PyCapsule, returns `cap`
|
||||||
|
# '''
|
||||||
|
# if not Capsule.valid(cap):
|
||||||
|
# if isinstance(cap, list):
|
||||||
|
# return list(map(wrap, cap))
|
||||||
|
# return cap # bypass if cap is not a PyCapsule and not a list
|
||||||
|
#
|
||||||
|
# cap = Capsule(cap)
|
||||||
|
# cls = cap.get_class()
|
||||||
|
# addr = cap.pointer
|
||||||
|
# name = cap.name
|
||||||
|
# # lookup cached object
|
||||||
|
# if cls in _cache and addr in _cache[cls]:
|
||||||
|
# obj = _cache[cls][addr]
|
||||||
|
# else:
|
||||||
|
# if not owned and cls._has_dtor():
|
||||||
|
# _addr2dtor[(name, addr)] = cls._delete_
|
||||||
|
# obj = cap.instantiate()
|
||||||
|
# _cache[cls][addr] = obj # cache it
|
||||||
|
# return obj
|
||||||
|
|
||||||
|
#def unwrap(obj):
|
||||||
'''Unwrap a Wrapper instance into the underlying PyCapsule.
|
'''Unwrap a Wrapper instance into the underlying PyCapsule.
|
||||||
If `obj` is not a Wrapper instance, returns `obj`.
|
If `obj` is not a Wrapper instance, returns `obj`.
|
||||||
'''
|
'''
|
||||||
if isinstance(obj, Wrapper):
|
# if isinstance(obj, Wrapper):
|
||||||
return obj._ptr
|
# return obj._ptr
|
||||||
else:
|
# else:
|
||||||
return obj
|
# return obj
|
||||||
|
|
||||||
|
|
||||||
def register_class(clsname):
|
def register_class(clsname):
|
||||||
def _wrapped(cls):
|
def _wrapped(cls):
|
||||||
_pyclasses[clsname] = cls
|
_pyclasses[clsname] = cls
|
||||||
return cls
|
return cls
|
||||||
|
|
||||||
return _wrapped
|
return _wrapped
|
||||||
|
|
||||||
|
|
||||||
class Wrapper(object):
|
class Wrapper(object):
|
||||||
|
|
||||||
__slots__ = '__capsule'
|
__slots__ = '__capsule'
|
||||||
|
|
||||||
def __init__(self, capsule):
|
def __init__(self, capsule):
|
||||||
self.__capsule = capsule
|
self.__capsule = capsule
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
if _addr2refct is None:
|
||||||
|
# System is tearing down
|
||||||
|
# No need to free anything
|
||||||
|
return
|
||||||
|
|
||||||
|
item = self.__capsule
|
||||||
|
addr = item.pointer
|
||||||
|
name = item.name
|
||||||
|
|
||||||
|
_addr2refct[addr] -= 1
|
||||||
|
refct = _addr2refct[addr]
|
||||||
|
assert refct >= 0, "RefCt drop below 0"
|
||||||
|
if refct == 0:
|
||||||
|
dtor = _addr2dtor.pop((name, addr), None)
|
||||||
|
if dtor is not None:
|
||||||
|
if not NO_DEBUG:
|
||||||
|
# Some globals in logger could be removed by python GC
|
||||||
|
# at interpreter teardown.
|
||||||
|
# That can cause exception raised and ignored message.
|
||||||
|
logger.debug('Destroy %s %s', name, hex(addr))
|
||||||
|
dtor(item.capsule)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _capsule(self):
|
def _capsule(self):
|
||||||
return self.__capsule
|
return self.__capsule
|
||||||
|
|
@ -198,7 +215,7 @@ class Wrapper(object):
|
||||||
return self._capsule == other._capsule
|
return self._capsule == other._capsule
|
||||||
|
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
return not(self == other)
|
return not (self == other)
|
||||||
|
|
||||||
def _downcast(self, newcls):
|
def _downcast(self, newcls):
|
||||||
return downcast(self, newcls)
|
return downcast(self, newcls)
|
||||||
|
|
@ -207,24 +224,25 @@ class Wrapper(object):
|
||||||
def _has_dtor(cls):
|
def _has_dtor(cls):
|
||||||
return hasattr(cls, '_delete_')
|
return hasattr(cls, '_delete_')
|
||||||
|
|
||||||
def downcast(obj, cls):
|
|
||||||
from . import _api
|
|
||||||
if type(obj) is cls:
|
|
||||||
return obj
|
|
||||||
fromty = obj._llvm_type_
|
|
||||||
toty = cls._llvm_type_
|
|
||||||
logger.debug("Downcast %s to %s" , fromty, toty)
|
|
||||||
fname = 'downcast_%s_to_%s' % (fromty, toty)
|
|
||||||
fname = fname.replace('::', '_')
|
|
||||||
if not hasattr(_api.downcast, fname):
|
|
||||||
fmt = "Downcast from %s to %s is not supported"
|
|
||||||
raise TypeError(fmt % (fromty, toty))
|
|
||||||
caster = getattr(_api.downcast, fname)
|
|
||||||
old = unwrap(obj)
|
|
||||||
new = caster(old)
|
|
||||||
used_to_own = has_ownership(old)
|
|
||||||
res = wrap(new, owned=not used_to_own)
|
|
||||||
if not res:
|
|
||||||
raise ValueError("Downcast failed")
|
|
||||||
return res
|
|
||||||
|
|
||||||
|
#def downcast(obj, cls):
|
||||||
|
# from . import _api
|
||||||
|
#
|
||||||
|
# if type(obj) is cls:
|
||||||
|
# return obj
|
||||||
|
# fromty = obj._llvm_type_
|
||||||
|
# toty = cls._llvm_type_
|
||||||
|
# logger.debug("Downcast %s to %s", fromty, toty)
|
||||||
|
# fname = 'downcast_%s_to_%s' % (fromty, toty)
|
||||||
|
# fname = fname.replace('::', '_')
|
||||||
|
# if not hasattr(_api.downcast, fname):
|
||||||
|
# fmt = "Downcast from %s to %s is not supported"
|
||||||
|
# raise TypeError(fmt % (fromty, toty))
|
||||||
|
# caster = getattr(_api.downcast, fname)
|
||||||
|
# old = unwrap(obj)
|
||||||
|
# new = caster(old)
|
||||||
|
# used_to_own = has_ownership(old)
|
||||||
|
# res = wrap(new, owned=not used_to_own)
|
||||||
|
# if not res:
|
||||||
|
# raise ValueError("Downcast failed")
|
||||||
|
# return res
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue