/* Copyright 2000-2001 ActiveState * * svrv_objects encapsulate a perl SvRV(). */ #include #include #include #ifdef __cplusplus extern "C" { #endif #include "thrd_ctx.h" #include "svrv_object.h" #include "perlmodule.h" #include "lang_lock.h" #include "lang_map.h" #include "try_perlapi.h" #ifdef MULTI_PERL static int owned_by(PySVRV *self, refcounted_perl *my_perl) { if (self->owned_by != my_perl) { PyErr_SetString(PyExc_ValueError, "perl reference accessed in wrong thread"); return 0; } return 1; } #define CHECK_OWNED(ret) do { \ ASSERT_LOCK_PYTHON; \ if (!owned_by(self, ctx->perl)) \ return (ret); \ } while (0) #define CHECK_OWNED_PY CHECK_OWNED((PyObject*)NULL) #define CHECK_OWNED_INT CHECK_OWNED(-1) #else /* MULTI_PERL */ #define CHECK_OWNED /* empty */ #define CHECK_OWNED_PY /* empty */ #define CHECK_OWNED_INT /* empty */ #endif /* MULTI_PERL */ PyObject* PySVRV_New(SV* rv) { dCTXP; PySVRV *self; ASSERT_LOCK_BOTH; self = PyObject_NEW(PySVRV, &SVRVtype); if (self == NULL) return NULL; self->rv = SvREFCNT_inc(rv); #ifdef MULTI_PERL self->owned_by = ctx->perl; ctx->perl->refcnt++; #endif self->methodname = NULL; self->gimme = G_SCALAR; /* printf("created svrv object %p\n", self); */ return (PyObject*)self; } static void pysvrv_dealloc(PySVRV *self) { /* printf("dead svrv object %p\n", self); */ dCTXP; #ifdef MULTI_PERL PerlInterpreter *old_perl = 0; if (my_perl != self->owned_by->my_perl) { old_perl = my_perl; my_perl = self->owned_by->my_perl; PERL_SET_CONTEXT(my_perl); } #endif ASSERT_LOCK_PYTHON; ENTER_PERL; SvREFCNT_dec(self->rv); Safefree(self->methodname); #ifdef MULTI_PERL if (old_perl) PERL_SET_CONTEXT(old_perl); if (--self->owned_by->refcnt == 0) { if (self->owned_by->thread_done) { free_perl(self->owned_by->my_perl); self->owned_by->my_perl = 0; ENTER_PYTHON; PyMem_Free((char*)(self->owned_by)); ENTER_PERL; } } self->owned_by = 0; #endif ENTER_PYTHON; #if PY_MAJOR_VERSION >= 1 && PY_MINOR_VERSION >= 6 PyObject_DEL(self); #else PyMem_DEL(self); #endif ASSERT_LOCK_PYTHON; } static PyObject* pysvrv_has_key(PySVRV *self, PyObject *args) { char *key; int keylen; int exists; dCTXP; ASSERT_LOCK_PYTHON; CHECK_OWNED_PY; if (!PyArg_ParseTuple(args, "s#:has_key", &key, &keylen)) return NULL; ENTER_PERL; SET_CUR_PERL; assert(SvTYPE(SvRV(self->rv)) == SVt_PVHV); exists = hv_exists((HV*)SvRV(self->rv), key, keylen); ENTER_PYTHON; return PyInt_FromLong(exists); } static PyObject* do_hash_kv(HV* hv, bool do_keys, bool do_values) { /* assumes we have the python lock only on entry */ register HE *entry; register PyObject* list; int i; int len; dCTXP; ASSERT_LOCK_PYTHON; assert(do_keys || do_values); ENTER_PERL; SET_CUR_PERL; len = HvKEYS(hv); ENTER_PYTHON; list = PyList_New(len); if (list == NULL) { ASSERT_LOCK_PYTHON; return NULL; } ENTER_PERL; i = 0; hv_iterinit(hv); while ( (entry = hv_iternext(hv))) { PyObject *k; if (do_keys) { I32 klen; char *kstr = hv_iterkey(entry, &klen); ENTER_PYTHON; k = PyString_FromStringAndSize(kstr, klen); if (k == NULL) goto FAIL; ENTER_PERL; } if (do_values) { SV* val_sv = hv_iterval(hv, entry); PyObject *v; ENTER_PYTHON; PERL_LOCK; v = sv2pyo(val_sv); PERL_UNLOCK; if (do_keys) { PyObject *t = PyTuple_New(2); if (t == NULL) { if (do_keys) Py_DECREF(k); goto FAIL; } /* These can't fail :-) */ PyTuple_SetItem(t, 0, k); PyTuple_SetItem(t, 1, v); v = t; } if (PyList_SetItem(list, i, v) == -1) { Py_DECREF(v); goto FAIL; } ENTER_PERL; } else if (PyList_SetItem(list, i, k) == -1) { ENTER_PYTHON; Py_DECREF(k); goto FAIL; }; i++; } ENTER_PYTHON; return list; FAIL: Py_DECREF(list); ASSERT_LOCK_PYTHON; return NULL; } static PyObject* pysvrv_keys(PySVRV *self, PyObject *args) { dCTXP; SET_CUR_PERL; CHECK_OWNED_PY; ASSERT_LOCK_PYTHON; if (!PyArg_NoArgs(args)) return NULL; assert(SvTYPE(SvRV(self->rv)) == SVt_PVHV); return do_hash_kv((HV*)SvRV(self->rv), TRUE, FALSE); } static PyObject* pysvrv_values(PySVRV *self, PyObject *args) { dCTXP; SET_CUR_PERL; CHECK_OWNED_PY; ASSERT_LOCK_PYTHON; if (!PyArg_NoArgs(args)) return NULL; assert(SvTYPE(SvRV(self->rv)) == SVt_PVHV); return do_hash_kv((HV*)SvRV(self->rv), FALSE, TRUE); } static PyObject* pysvrv_items(PySVRV *self, PyObject *args) { #ifdef DEBUGGING dCTXP; SET_CUR_PERL; #endif ASSERT_LOCK_PYTHON; if (!PyArg_NoArgs(args)) return NULL; assert(SvTYPE(SvRV(self->rv)) == SVt_PVHV); return do_hash_kv((HV*)SvRV(self->rv), TRUE, TRUE); } static PyObject* pysvrv_update(PySVRV *self, PyObject *args) { PyObject *o; PyObject *items; int i; PyObject *elem; HV* hv; dCTXP; ASSERT_LOCK_PYTHON; CHECK_OWNED_PY; if (!PyArg_ParseTuple(args, "O:update", &o)) goto FAIL; if (!PyMapping_Check(o)) { PyErr_SetString(PyExc_TypeError, "hash.update() argument must be a mapping object"); goto FAIL; } items = PyMapping_Items(o); if (items == NULL) goto FAIL; if (!PyList_Check(items)) { Py_DECREF(items); PyErr_SetString(PyExc_SystemError, "PyMapping_Items did not return a list"); } ENTER_PERL; SET_CUR_PERL; assert(SvTYPE(SvRV(self->rv)) == SVt_PVHV); hv = (HV*)SvRV(self->rv); ENTER_PYTHON; for (i = 0; (elem = PyList_GetItem(items, i)); i++) { PyObject* key; PyObject* val; SV* key_sv; SV* val_sv; ASSERT_LOCK_PYTHON; if (!PySequence_Check(elem)) continue; key = PySequence_GetItem(elem, 0); if (!key) { PyErr_Clear(); continue; } val = PySequence_GetItem(elem, 1); if (!val) { PyErr_Clear(); continue; } PERL_LOCK; key_sv = pyo2sv(key); val_sv = pyo2sv(val); PYTHON_UNLOCK; if (!hv_store_ent(hv, key_sv, val_sv, 0)) SvREFCNT_dec(val_sv); SvREFCNT_dec(key_sv); ENTER_PYTHON; } PyErr_Clear(); /* index error */ Py_DECREF(items); ASSERT_LOCK_PYTHON; Py_INCREF(Py_None); return Py_None; FAIL: ASSERT_LOCK_PYTHON; return NULL; } static PyObject* pysvrv_clear(PySVRV *self, PyObject *args) { dCTXP; ASSERT_LOCK_PYTHON; CHECK_OWNED_PY; if (!PyArg_NoArgs(args)) return NULL; ENTER_PERL; SET_CUR_PERL; assert(SvTYPE(SvRV(self->rv)) == SVt_PVHV); hv_clear((HV*)SvRV(self->rv)); ENTER_PYTHON; Py_INCREF(Py_None); return Py_None; } static PyObject* pysvrv_copy(PySVRV *self, PyObject *args) { HV* hv; HV* new_hv; HE* entry; SV* sv; PyObject *pyo; dCTXP; ASSERT_LOCK_PYTHON; CHECK_OWNED_PY; if (!PyArg_NoArgs(args)) return NULL; ENTER_PERL; SET_CUR_PERL; assert(SvTYPE(SvRV(self->rv)) == SVt_PVHV); hv = (HV*)SvRV(self->rv); new_hv = newHV(); hv_iterinit(hv); while ( (entry = hv_iternext(hv))) { sv = newSVsv(HeVAL(entry)); if (!hv_store_ent(new_hv, hv_iterkeysv(entry), sv, 0)) SvREFCNT_dec(sv); } sv = newRV_noinc((SV*)new_hv); ENTER_PYTHON; PERL_LOCK; pyo = PySVRV_New(sv); SvREFCNT_dec(sv); /* since PySVRV_New incremented it */ PERL_UNLOCK; ASSERT_LOCK_PYTHON; return pyo; } static PyObject* pysvrv_get(PySVRV *self, PyObject *args) { char *key; int keylen; PyObject *failobj = Py_None; SV** svp; dCTXP; ASSERT_LOCK_PYTHON; CHECK_OWNED_PY; if (!PyArg_ParseTuple(args, "s#|O:get", &key, &keylen, &failobj)) return NULL; ENTER_PERL; SET_CUR_PERL; assert(SvTYPE(SvRV(self->rv)) == SVt_PVHV); svp = hv_fetch((HV*)SvRV(self->rv), key, keylen, 0); ENTER_PYTHON; if (svp) { PyObject *tmp; PERL_LOCK; tmp = sv2pyo(*svp); PERL_UNLOCK; return tmp; } Py_INCREF(failobj); ASSERT_LOCK_PYTHON; return failobj; } /* The following function as adapted from Larry Wall's pp_splice in * the perl source. If will remove len elements at offset and * make room for newlen new elements by filling the space with &sv_undef. */ static int array_splice(AV* av, I32 offset, I32 len, I32 newlen) { /* This function assumes that we hold the perl lock only, when it is * called. On normal return it will not change lock status, but on * errors it swith to python lock mode, set execption state and return -1. */ I32 asize; I32 diff, after, i; SV **src; SV **dst; dCTXP; ASSERT_LOCK_PERL; SET_CUR_PERL; /* #define SPLICE_DEBUG /* */ asize = av_len(av) + 1; if (offset < 0) offset += asize; if (offset < 0 || offset > asize) { ENTER_PYTHON; PyErr_SetString(PyExc_IndexError, "perl array index out of range"); return -1; } if (len < 0) { len += asize - offset; if (len < 0) len = 0; } if (newlen < 0) { ENTER_PYTHON; PyErr_BadInternalCall(); return -1; } after = asize - offset - len; if (after < 0) { len += after; after = 0; if (!AvALLOC(av)) av_extend(av, 0); } diff = newlen - len; if (newlen && !AvREAL(av) && AvREIFY(av)) av_reify(av); #ifdef SPLICE_DEBUG printf("splice(offset=%d, len=%d, diff=%d, after=%d, fill=%d, max=%d, pre=%d)\n", offset, len, diff, after, AvFILLp(av), AvMAX(av), AvARRAY(av) - AvALLOC(av)); #endif /* free old stuff */ src = &AvARRAY(av)[offset]; for (i = len; i; i--) { #ifdef SPLICE_DEBUG printf(" free #%d\n", src - AvARRAY(av)); #endif SvREFCNT_dec(*src); *src = &PL_sv_undef; src++; } if (diff < 0) { /* shrinking the area */ AvFILLp(av) += diff; if (offset < after) { /* easier to pull up */ if (offset) /* esp. if nothing to pull */ Move(AvARRAY(av), AvARRAY(av)-diff, offset, SV*); SvPVX(av) = (char*)(AvARRAY(av) - diff); AvMAX(av) += diff; dst = AvARRAY(av) + diff; } else { if (after) { /* anything to pull down? */ src = AvARRAY(av) + offset + len; dst = src + diff; /* diff is negative */ Move(src, dst, after, SV*); } dst = &AvARRAY(av)[AvFILLp(av)+1]; } i = -diff; } else if (diff > 0) { /* expanding */ /* push up or down? */ if (offset < after && diff <= AvARRAY(av) - AvALLOC(av)) { if (offset) { src = AvARRAY(av); dst = src - diff; Move(src, dst, offset, SV*); } SvPVX(av) = (char*)(AvARRAY(av) - diff); /* diff is positive */ AvMAX(av) += diff; AvFILLp(av) += diff; dst = AvARRAY(av) + offset; } else { if (AvFILLp(av) + diff > AvMAX(av)) /* oh, well */ av_extend(av, AvFILLp(av) + diff); AvFILLp(av) += diff; if (after) { src = AvARRAY(av) + offset + len; dst = src + diff; Move(src, dst, after, SV*); dst = src; } else dst = AvARRAY(av) + AvFILLp(av); } i = diff; } /* clear moved away area */ while (i) { dst[--i] = &PL_sv_undef; #ifdef SPLICE_DEBUG printf(" clear #%d\n", dst - AvARRAY(av) + i); #endif } #ifdef SPLICE_DEBUG printf(" -->(fill=%d, max=%d, pre=%d)\n", AvFILLp(av), AvMAX(av), AvARRAY(av) - AvALLOC(av)); #endif ASSERT_LOCK_PERL; return 0; } static PyObject * array_item(AV* av, I32 index) { /* Assumes python lock */ SV** svp; I32 size; dCTXP; ASSERT_LOCK_PYTHON; ENTER_PERL; SET_CUR_PERL; svp = av_fetch(av, index, 0); if (svp) { PyObject *tmp; int status = try_SvGETMAGIC(*svp); ENTER_PYTHON; if (status == -1) goto FAIL; PERL_LOCK; tmp = sv2pyo(*svp); PERL_UNLOCK; ASSERT_LOCK_PYTHON; return tmp; } ENTER_PYTHON; if (PyErr_Occurred()) goto FAIL; /* av_fetch also returns 0 for empty slots filled with PL_av_undef, * so we need to compensate for that by testing if we actually are * within bounds. */ ENTER_PERL; size = try_array_len(av); ENTER_PYTHON; if (size == -1) goto FAIL; if (index < size && index >= -size) { ASSERT_LOCK_PYTHON; return Py_BuildValue(""); } PyErr_SetString(PyExc_IndexError, "perl array index out of range"); FAIL: ASSERT_LOCK_PYTHON; return NULL; } static PyObject* pysvrv_append(PySVRV *self, PyObject *args) { PyObject *v; AV* av; SV* sv; SV** svp; dCTXP; ASSERT_LOCK_PYTHON; CHECK_OWNED_PY; if (!PyArg_ParseTuple(args, "O:append", &v)) return NULL; PERL_LOCK; SET_CUR_PERL; sv = pyo2sv(v); PYTHON_UNLOCK; assert(SvTYPE(SvRV(self->rv)) == SVt_PVAV); av = (AV*)SvRV(self->rv); svp = av_store(av, av_len(av)+1, sv); if (!svp) { SvREFCNT_dec(sv); ENTER_PYTHON; PyErr_SetString(PyExc_RuntimeError, "av_store failed"); return NULL; } ENTER_PYTHON; Py_INCREF(Py_None); ASSERT_LOCK_PYTHON; return Py_None; } static PyObject* pysvrv_insert(PySVRV *self, PyObject *args) { int i; PyObject *v; AV* av; SV* sv; SV** svp; dCTXP; ASSERT_LOCK_PYTHON; CHECK_OWNED_PY; if (!PyArg_ParseTuple(args, "iO:insert", &i, &v)) return NULL; ENTER_PERL; SET_CUR_PERL; assert(SvTYPE(SvRV(self->rv)) == SVt_PVAV); av = (AV*)SvRV(self->rv); if (array_splice(av, i, 0, 1) == -1) { ASSERT_LOCK_PYTHON; return NULL; } ENTER_PYTHON; PERL_LOCK; sv = pyo2sv(v); PYTHON_UNLOCK; svp = av_store(av, i, sv); if (!svp) { SvREFCNT_dec(sv); ENTER_PYTHON; PyErr_SetString(PyExc_RuntimeError, "av_store failed"); ASSERT_LOCK_PYTHON; return NULL; } ENTER_PYTHON; Py_INCREF(Py_None); ASSERT_LOCK_PYTHON; return Py_None; } static PyObject* pysvrv_extend(PySVRV *self, PyObject *args) { PyObject *o; AV* av; int n, i; STRLEN size; dCTXP; ASSERT_LOCK_PYTHON; CHECK_OWNED_PY; if (!PyArg_ParseTuple(args, "O:extend", &o)) goto FAIL; if (!PySequence_Check(o)) { PyErr_SetString(PyExc_TypeError, "array.extend() argument must be a sequence"); goto FAIL; } n = PySequence_Length(o); if (n < 0) goto FAIL; ENTER_PERL; SET_CUR_PERL; assert(SvTYPE(SvRV(self->rv)) == SVt_PVAV); av = (AV*)SvRV(self->rv); size = av_len(av) + 1; if (n) av_extend(av, (size-1) + n); /* Special case for a.extend(a) */ if (PySVRV_Check(o) && SvRV(((PySVRV*)o)->rv) == (SV*)av) { SV** svp; for (i = 0; i < size; i++) { svp = av_fetch(av, i, 0); if (svp) { if (av_store(av, size + i, *svp)) SvREFCNT_inc(*svp); } } ENTER_PYTHON; goto DONE; } ENTER_PYTHON; for (i = 0;; i++) { PyObject *item; ASSERT_LOCK_PYTHON; item = PySequence_GetItem(o, i); if (item) { SV* item_sv; PERL_LOCK; item_sv = pyo2sv(item); PYTHON_UNLOCK; if (!av_store(av, size + i, item_sv)) { SvREFCNT_dec(item_sv); ENTER_PYTHON; PyErr_SetString(PyExc_RuntimeError, "av_store failed"); goto FAIL; } ENTER_PYTHON; } else { if (PyErr_ExceptionMatches(PyExc_IndexError)) { PyErr_Clear(); break; } /* Something else bad happened */ goto FAIL; } } DONE: ASSERT_LOCK_PYTHON; Py_INCREF(Py_None); return Py_None; FAIL: /* XXX can we undo whatever we already might have stored in av??? */ ASSERT_LOCK_PYTHON; return NULL; } static PyObject* pysvrv_pop(PySVRV *self, PyObject *args) { AV* av; I32 len; int i = -1; SV* sv; dCTXP; ASSERT_LOCK_PYTHON; CHECK_OWNED_PY; if (!PyArg_ParseTuple(args, "|i:pop", &i)) return NULL; ENTER_PERL; SET_CUR_PERL; assert(SvTYPE(SvRV(self->rv)) == SVt_PVAV); av = (AV*)SvRV(self->rv); len = av_len(av); if (len == -1) { ENTER_PYTHON; PyErr_SetString(PyExc_IndexError, "pop from empty list"); ASSERT_LOCK_PYTHON; return NULL; } if (i == -1 || i == len) { SV* tmp = av_pop(av); PyObject *o; ENTER_PYTHON; PERL_LOCK; o = sv2pyo(tmp); PERL_UNLOCK; ASSERT_LOCK_PYTHON; return o; } else { PyObject* pyo; ENTER_PYTHON; pyo = array_item(av, i); if (!pyo) { ASSERT_LOCK_PYTHON; return NULL; } ENTER_PERL; if (array_splice(av, i, 1, 0) == -1) { Py_DECREF(pyo); ASSERT_LOCK_PYTHON; return NULL; } ENTER_PYTHON; return pyo; } } static int array_index(AV* av, PyObject *v) { I32 i; I32 len; SV** svp; dCTXP; ASSERT_LOCK_PERL; SET_CUR_PERL; len = av_len(av); for (i = 0; i <= len; i++) { ASSERT_LOCK_PERL; svp = av_fetch(av, i, 0); if (svp) { PyObject *x; int cmp; ENTER_PYTHON; PERL_LOCK; x = sv2pyo(*svp); PERL_UNLOCK; cmp = PyObject_Compare(x, v); Py_DECREF(x); if (cmp == 0) { ENTER_PERL; return i; } if (cmp == -1 && PyErr_Occurred()) { ENTER_PERL; return -1; } ENTER_PERL; } else if (v == Py_None) { ASSERT_LOCK_PERL; return i; } } ASSERT_LOCK_PERL; return -1; } static PyObject* pysvrv_remove(PySVRV *self, PyObject *args) { AV* av; PyObject *v; int index; dCTXP; ASSERT_LOCK_PYTHON; CHECK_OWNED_PY; if (!PyArg_ParseTuple(args, "O:index", &v)) return NULL; ENTER_PERL; SET_CUR_PERL; assert(SvTYPE(SvRV(self->rv)) == SVt_PVAV); av = (AV*)SvRV(self->rv); index = array_index(av, v); if (index == -1) { ENTER_PYTHON; if (!PyErr_Occurred()) PyErr_SetString(PyExc_ValueError, "perlarray.remove(x): x not in list"); ASSERT_LOCK_PYTHON; return NULL; } array_splice(av, index, 1, 0); ENTER_PYTHON; Py_INCREF(Py_None); ASSERT_LOCK_PYTHON; return Py_None; } static PyObject* pysvrv_index(PySVRV *self, PyObject *args) { AV* av; PyObject *v; int index; dCTXP; ASSERT_LOCK_PYTHON; CHECK_OWNED_PY; if (!PyArg_ParseTuple(args, "O:index", &v)) return NULL; ENTER_PERL; SET_CUR_PERL; assert(SvTYPE(SvRV(self->rv)) == SVt_PVAV); av = (AV*)SvRV(self->rv); index = array_index(av, v); ENTER_PYTHON; if (index == -1) { if (!PyErr_Occurred()) PyErr_SetString(PyExc_ValueError, "perlarray.index(x): x not in list"); ASSERT_LOCK_PYTHON; return NULL; } ASSERT_LOCK_PYTHON; return PyInt_FromLong((long)index); } static PyObject* pysvrv_count(PySVRV *self, PyObject *args) { AV* av; I32 len, i; PyObject *v; SV** svp; int count = 0; dCTXP; ASSERT_LOCK_PYTHON; CHECK_OWNED_PY; SET_CUR_PERL; if (!PyArg_ParseTuple(args, "O:count", &v)) return NULL; ENTER_PERL; assert(SvTYPE(SvRV(self->rv)) == SVt_PVAV); av = (AV*)SvRV(self->rv); len = av_len(av); for (i = 0; i <= len; i++) { ASSERT_LOCK_PERL; svp = av_fetch(av, i, 0); if (svp) { PyObject *x; int cmp; ENTER_PYTHON; PERL_LOCK; x = sv2pyo(*svp); PERL_UNLOCK; cmp = PyObject_Compare(x, v); Py_DECREF(x); if (cmp == 0) count++; if (cmp == -1 && PyErr_Occurred()) { ASSERT_LOCK_PYTHON; return NULL; } ENTER_PERL; } else if (v == Py_None) count++; } ENTER_PYTHON; return PyInt_FromLong((long)count); } static PyObject* pysvrv_reverse(PySVRV *self, PyObject *args) { AV* av; I32 len, i; dCTXP; ASSERT_LOCK_PYTHON; CHECK_OWNED_PY; if (!PyArg_NoArgs(args)) return NULL; ENTER_PERL; SET_CUR_PERL; assert(SvTYPE(SvRV(self->rv)) == SVt_PVAV); av = (AV*)SvRV(self->rv); if (SvREADONLY(av)) { ENTER_PYTHON; PyErr_SetString(PyExc_TypeError, "read only array can be modified"); ASSERT_LOCK_PYTHON; return NULL; } if (SvTIED_mg((SV*)av, 'P')) { ENTER_PYTHON; PyErr_SetString(PyExc_TypeError, "tied array"); ASSERT_LOCK_PYTHON; return NULL; } len = av_len(av); if (len > 0) { for (i = (len-1) / 2; i >= 0; i--) { SV* tmp; I32 other = len - i; /* swap them */ tmp = AvARRAY(av)[i]; AvARRAY(av)[i] = AvARRAY(av)[other]; AvARRAY(av)[other] = tmp; } } ENTER_PYTHON; Py_INCREF(Py_None); ASSERT_LOCK_PYTHON; return Py_None; } static PyObject* pysvrv_sort(PySVRV *self, PyObject *args) { ASSERT_LOCK_PYTHON; PyErr_SetString(PyExc_NotImplementedError, "array sort"); return NULL; } /* only useful for debugging (and test suite) */ static PyObject* pysvrv_av_alloc(PySVRV *self, PyObject *args) { AV* av; PyObject *t; long left, middle, right; dCTXP; ASSERT_LOCK_PYTHON; SET_CUR_PERL; CHECK_OWNED_PY; if (!PyArg_NoArgs(args)) return NULL; ENTER_PERL; assert(SvTYPE(SvRV(self->rv)) == SVt_PVAV); av = (AV*)SvRV(self->rv); left = AvARRAY(av) - AvALLOC(av); /* extra allocated at beginning */ middle = AvFILLp(av) + 1; /* used */ right = AvMAX(av) - AvFILLp(av); /* extra allocated at end */ ENTER_PYTHON; t = PyTuple_New(3); if (t == NULL) return NULL; PyTuple_SetItem(t, 0, PyInt_FromLong(left)); PyTuple_SetItem(t, 1, PyInt_FromLong(middle)); PyTuple_SetItem(t, 2, PyInt_FromLong(right)); ASSERT_LOCK_PYTHON; return t; } static PyMethodDef mapp_methods[] = { {"has_key", (PyCFunction)pysvrv_has_key, METH_VARARGS}, {"keys", (PyCFunction)pysvrv_keys, 0}, {"items", (PyCFunction)pysvrv_items, 0}, {"values", (PyCFunction)pysvrv_values, 0}, {"update", (PyCFunction)pysvrv_update, METH_VARARGS}, {"clear", (PyCFunction)pysvrv_clear, 0}, {"copy", (PyCFunction)pysvrv_copy, 0}, {"get", (PyCFunction)pysvrv_get, METH_VARARGS}, {NULL, NULL} /* sentinel */ }; static PyMethodDef list_methods[] = { {"append", (PyCFunction)pysvrv_append, METH_VARARGS}, {"insert", (PyCFunction)pysvrv_insert, METH_VARARGS}, {"extend", (PyCFunction)pysvrv_extend, METH_VARARGS}, {"pop", (PyCFunction)pysvrv_pop, METH_VARARGS}, {"remove", (PyCFunction)pysvrv_remove, METH_VARARGS}, {"index", (PyCFunction)pysvrv_index, METH_VARARGS}, {"count", (PyCFunction)pysvrv_count, METH_VARARGS}, {"reverse", (PyCFunction)pysvrv_reverse, 0}, {"sort", (PyCFunction)pysvrv_sort, METH_VARARGS}, {"av_alloc",(PyCFunction)pysvrv_av_alloc,0}, {NULL, NULL} /* sentinel */ }; static PyObject* pysvrv_getattr(PySVRV *self, char *name) { PyObject *val; dCTXP; ASSERT_LOCK_PYTHON; CHECK_OWNED_PY; SET_CUR_PERL; if (strcmp(name, "__wantarray__") == 0) { if (self->gimme == G_VOID) val = Py_BuildValue(""); /* None */ else val = PyInt_FromLong((long)(self->gimme == G_ARRAY)); } else if (strcmp(name, "__methodname__") == 0) { if (self->methodname) val = PyString_FromString(self->methodname); else val = Py_BuildValue(""); /* None */ } else if (strcmp(name, "__class__") == 0) { SV *sv; ENTER_PERL; sv = SvRV(self->rv); if (SvOBJECT(sv)) { char *klass = HvNAME(SvSTASH(sv)); ENTER_PYTHON; val = PyString_FromString(klass); } else { ENTER_PYTHON; val = Py_BuildValue(""); } } else if (strcmp(name, "__type__") == 0) { char *tmp; ENTER_PERL; tmp = sv_reftype(SvRV(self->rv), 0); ENTER_PYTHON; val = PyString_FromString(tmp); } else if (strcmp(name, "__value__") == 0) { SV *sv = SvRV(self->rv); switch (SvTYPE(sv)) { case SVt_PVAV: case SVt_PVHV: case SVt_PVCV: PyErr_SetString(PyExc_AttributeError, name); val = NULL; break; default: PERL_LOCK; val = sv2pyo(sv); PERL_UNLOCK; } } else if (strcmp(name, "__readonly__") == 0) { val = PyInt_FromLong(SvREADONLY(SvRV(self->rv)) != 0); } else if (self->methodname) { PyErr_SetString(PyExc_AttributeError, name); val = NULL; } else if (SvOBJECT(SvRV(self->rv))) { PySVRV *method_obj; int len; PERL_LOCK; method_obj = (PySVRV *)PySVRV_New(self->rv); len = strlen(name); New(999, method_obj->methodname, len+1, char); Copy(name, method_obj->methodname, len+1, char); if (len > 6 && strEQ(name+len-6, "_tuple")) { method_obj->methodname[len-6] = '\0'; method_obj->gimme = G_ARRAY; } else { method_obj->gimme = self->gimme; } PERL_UNLOCK; val = (PyObject *)method_obj; } else if (SvTYPE(SvRV(self->rv)) == SVt_PVAV) { val = Py_FindMethod(list_methods, (PyObject *)self, name); } else if (SvTYPE(SvRV(self->rv)) == SVt_PVHV) { val = Py_FindMethod(mapp_methods, (PyObject *)self, name); } else { PyErr_SetString(PyExc_AttributeError, name); val = NULL; } ASSERT_LOCK_PYTHON; return val; } static int pysvrv_setattr(PySVRV *self, char *name, PyObject *val) { int status; dCTXP; ASSERT_LOCK_PYTHON; CHECK_OWNED_INT; SET_CUR_PERL; if (strcmp(name, "__wantarray__") == 0) { if (val == Py_None) self->gimme = G_VOID; else self->gimme = PyObject_IsTrue(val) ? G_ARRAY : G_SCALAR; status = 0; } else if (strcmp(name, "__methodname__") == 0) { if (PyString_Check(val)) { PERL_LOCK; Safefree(self->methodname); New(998, self->methodname, PyString_GET_SIZE(val)+1, char); Copy(PyString_AS_STRING(val), self->methodname, PyString_GET_SIZE(val)+1, char); PERL_UNLOCK; status = 0; } else { PyErr_SetString(PyExc_TypeError, "__methodname__ must be string"); status = -1; } } else if (strcmp(name, "__class__") == 0) { if (PyString_Check(val)) { char *klass = PyString_AsString(val); ENTER_PERL; sv_bless(self->rv, gv_stashpv(klass, 1)); ENTER_PYTHON; status = 0; } else if (val == Py_None) { /* unbless */ PyErr_SetString(PyExc_NotImplementedError, "unbless"); status = -1; } else { PyErr_SetString(PyExc_TypeError, "__class__ must be string"); status = -1; } } else if (strcmp(name, "__value__") == 0) { SV *sv; SV *val_sv; PERL_LOCK; sv = SvRV(self->rv); switch (SvTYPE(sv)) { case SVt_PVAV: case SVt_PVHV: case SVt_PVCV: PERL_UNLOCK; PyErr_SetString(PyExc_AttributeError, name); status = -1; break; default: val_sv = pyo2sv(val); SvSetMagicSV(sv, val_sv); SvREFCNT_dec(val_sv); PERL_UNLOCK; status = 0; } } else if (strcmp(name, "__readonly__") == 0) { /* to give write access to this attribute is not really a good idea, * but it can be fun for experimentation. */ if (PyObject_IsTrue(val)) SvREADONLY_on(SvRV(self->rv)); else SvREADONLY_off(SvRV(self->rv)); status = 0; } else { PyErr_SetString(PyExc_AttributeError, name); status = -1; } ASSERT_LOCK_PYTHON; return status; } static PyObject* pysvrv_call(PySVRV *self, PyObject *arg, PyObject *kw) { dCTX; PyObject *res; ASSERT_LOCK_PYTHON; CHECK_OWNED_PY; res = call_perl(self->methodname, self->rv, self->gimme, arg, kw); ASSERT_LOCK_PYTHON; return res; } static PyObject* pysvrv_repr(PySVRV *self) { SV* tmp_sv; SV* sv; PyObject* o; dCTXP; ASSERT_LOCK_PYTHON; /* We don't CHECK_OWNED here and hope for the best :-) */ ENTER_PERL; SET_CUR_PERL; tmp_sv = newSVpvn("<", 1); sv = SvRV(self->rv); if (self->methodname) { sv_catpvf(tmp_sv, "method %s of ", self->methodname); } sv_catpvn(tmp_sv, "perl ", 5); if (SvOBJECT(sv)) { sv_catpvf(tmp_sv, "%s=", HvNAME(SvSTASH(sv))); } sv_catpvf(tmp_sv, "%s(0x%p) ref at %p", sv_reftype(sv, 0), sv, self); #if 0 sv_catpvf(tmp_sv, " (%s)", self->gimme == G_VOID ? "G_VOID" : self->gimme == G_SCALAR ? "G_SCALAR" : self->gimme == G_ARRAY ? "G_ARRAY" : "?"); #endif sv_catpvn(tmp_sv, ">", 1); ENTER_PYTHON; o = PyString_FromStringAndSize(SvPVX(tmp_sv), SvCUR(tmp_sv)); SvREFCNT_dec(tmp_sv); ASSERT_LOCK_PYTHON; return o; } static void type_error(char *msg, SV* sv) { SV* tmp; dCTXP; ASSERT_LOCK_PYTHON; ENTER_PERL; SET_CUR_PERL; tmp = newSVpvf("%s perl %s", msg, sv_reftype(sv, 0)); ENTER_PYTHON; PyErr_SetString(PyExc_TypeError, SvPVX(tmp)); PERL_LOCK; SvREFCNT_dec(tmp); PERL_UNLOCK; ASSERT_LOCK_PYTHON; } static int pysvrv_length(PySVRV *self) { SV* sv; int len; dCTX; ASSERT_LOCK_PYTHON; CHECK_OWNED_INT; ENTER_PERL; sv = SvRV(self->rv); if (SvTYPE(sv) == SVt_PVAV) { len = try_array_len((AV*)sv); } else if (SvTYPE(sv) == SVt_PVHV) { len = HvKEYS(sv); /* XXX support tied hashes */ } else { ENTER_PYTHON; type_error("Can't count", sv); len = -1; ENTER_PERL; /* just so we can change back :-( */ } ENTER_PYTHON; ASSERT_LOCK_PYTHON; return len; } static int pysvrv_nonzero(PySVRV *self) { SV* sv; int v; dCTX; ASSERT_LOCK_PYTHON; CHECK_OWNED_INT; ENTER_PERL; sv = SvRV(self->rv); if (SvTYPE(sv) == SVt_PVAV) { v = try_array_len((AV*)sv) != 0; } else if (SvTYPE(sv) == SVt_PVHV) { v = HvKEYS(sv) != 0; /* XXX support tied hashes */ } else { v = 1; } ENTER_PYTHON; ASSERT_LOCK_PYTHON; return v; } static PyObject * pysvrv_item(PySVRV *self, int index) { SV* sv; PyObject *item; dCTX; ASSERT_LOCK_PYTHON; CHECK_OWNED_PY; sv = SvRV(self->rv); if (SvTYPE(sv) == SVt_PVAV) { item = array_item((AV*)sv, index); } else { type_error("Can't sequence index", sv); item = NULL; } ASSERT_LOCK_PYTHON; return item; } static PyObject* pysvrv_subscript(PySVRV *self, PyObject *key) { SV* sv; PyObject *val = key; /* just something different than null */ dCTXP; ASSERT_LOCK_PYTHON; SET_CUR_PERL; CHECK_OWNED_PY; assert(key); sv = SvRV(self->rv); if (SvTYPE(sv) == SVt_PVAV) { I32 index; if (PyInt_Check(key)) index = PyInt_AsLong(key); else if (PyLong_Check(key)) { index = PyLong_AsLong(key); if (index == -1 && PyErr_Occurred()) val = NULL; } else { PyErr_SetString(PyExc_TypeError, "perl array index must be integer"); val = NULL; } if (val) val = array_item((AV*)sv, index); } else if (SvTYPE(sv) == SVt_PVHV) { HV* hv = (HV*)sv; if (PyString_Check(key)) { SV** svp; ENTER_PERL; svp = hv_fetch(hv, PyString_AsString(key), PyString_Size(key), 0); if (svp) { SvGETMAGIC(*svp); PYTHON_LOCK; val = sv2pyo(*svp); PERL_UNLOCK; } else { ENTER_PYTHON; PyErr_SetObject(PyExc_KeyError, key); val = NULL; } } else { PyErr_SetString(PyExc_TypeError, "perl hash key must be string"); val = NULL; } } else { type_error("Can't index", sv); val = NULL; } assert(val != key); ASSERT_LOCK_PYTHON; return val; } static int pysvrv_ass_sub(PySVRV *self, PyObject *key, PyObject *val) { SV* sv; int status; /* return value */ dCTXP; ASSERT_LOCK_PYTHON; CHECK_OWNED_INT; SET_CUR_PERL; sv = SvRV(self->rv); if (SvTYPE(sv) == SVt_PVAV) { AV* av = (AV*)sv; I32 len; I32 index; SV* val_sv; SV** svp; if (PyInt_Check(key)) index = PyInt_AsLong(key); else if (PyLong_Check(key)) { index = PyLong_AsLong(key); if (index == -1 && PyErr_Occurred()) goto FAIL; } else { PyErr_SetString(PyExc_TypeError, "perl array index must be integer"); goto FAIL; } ENTER_PERL; if (!val) { /* delete */ status = array_splice(av, index, 1, 0); if (status == -1) ENTER_PERL; /* Blææ!! */ } else { len = av_len(av); ENTER_PYTHON; if (index < (-len-1) || index > len) { PyErr_SetString(PyExc_IndexError, "perl array assignment index out of range"); goto FAIL; } PERL_LOCK; val_sv = pyo2sv(val); PYTHON_UNLOCK; svp = av_store(av, index, val_sv); if (!svp) { SvREFCNT_dec(val_sv); ENTER_PYTHON; PyErr_SetString(PyExc_RuntimeError, "av_store failed"); goto FAIL; } status = 0; } ENTER_PYTHON; } else if (SvTYPE(sv) == SVt_PVHV) { HV* hv = (HV*)sv; if (PyString_Check(key)) { char *key_str = PyString_AsString(key); int key_len = PyString_Size(key); if (val) { SV* val_sv; SV** svp; PERL_LOCK; val_sv = pyo2sv(val); PYTHON_UNLOCK; svp = hv_store(hv, key_str, key_len, val_sv, 0); if (svp) { if (try_SvSETMAGIC(*svp) == -1) { ENTER_PYTHON; goto FAIL; } } ENTER_PYTHON; if (!svp) { SvREFCNT_dec(val_sv); PyErr_SetString(PyExc_RuntimeError, "av_store failed"); goto FAIL; } } else { SV* sv; int key_deleted; ENTER_PERL; /* Since hv_delete gives us a mortal copy, we set up a block * to get rid of it. */ ENTER; SAVETMPS; sv = hv_delete(hv, key_str, key_len, 0); key_deleted = (sv != NULL); FREETMPS; /* sv invalidated */ LEAVE; ENTER_PYTHON; if (!key_deleted) { PyErr_SetObject(PyExc_KeyError, key); goto FAIL; } } status = 0; } else { PyErr_SetString(PyExc_TypeError, "perl hash key must be string"); status = -1; } } else { type_error("Can't index", sv); FAIL: status = -1; } ASSERT_LOCK_PYTHON; return status; } static PyObject * pysvrv_concat(PySVRV *self, PyObject *other) { SV* sv1; PyObject *pyo_res; dCTXP; ASSERT_LOCK_PYTHON; CHECK_OWNED_PY; SET_CUR_PERL; sv1 = SvRV(self->rv); if (SvTYPE(sv1) == SVt_PVAV) { if (other && PySVRV_Check(other)) { SV* sv2 = SvRV(((PySVRV*)other)->rv); #ifdef MULTI_PERL if (!owned_by((PySVRV*)other, ctx->perl)) { pyo_res = NULL; goto DONE; } #endif if (SvTYPE(sv2) == SVt_PVAV) { AV* av1 = (AV*)sv1; AV* av2 = (AV*)sv2; AV* res; I32 i, len1, len2; SV** svp; SV* sv; ENTER_PERL; res = newAV(); len1 = av_len(av1) + 1; len2 = av_len(av2) + 1; av_extend(res, len1 + len2 - 1); for (i = 0; i < len1; i++) { svp = av_fetch(av1, i, 0); if (svp) { sv = newSVsv(*svp); if (!av_store(res, i, sv)) SvREFCNT_dec(sv); } } for (i = 0; i < len2; i++) { svp = av_fetch(av2, i, 0); if (svp) { sv = newSVsv(*svp); if (!av_store(res, i+len1, sv)) SvREFCNT_dec(sv); } } sv = newRV_noinc((SV*)res); ENTER_PYTHON; PERL_LOCK; pyo_res = PySVRV_New(sv); SvREFCNT_dec(sv); /* since PySVRV_New incremented it */ PERL_UNLOCK; goto DONE; } } PyErr_SetString(PyExc_TypeError, "illegal argument type for perl array concatenation"); pyo_res = NULL; } else { type_error("Can't concat", sv1); pyo_res = NULL; } DONE: ASSERT_LOCK_PYTHON; return pyo_res; } static PyObject * pysvrv_repeat(PySVRV *self, int n) { SV* sv; PyObject *pyo_res; dCTXP; ASSERT_LOCK_PYTHON; CHECK_OWNED_PY; ENTER_PERL; SET_CUR_PERL; sv = SvRV(self->rv); if (SvTYPE(sv) == SVt_PVAV) { AV* av = (AV*)sv; I32 size = av_len(av)+1; AV* res; I32 res_size; I32 i, j; SV** svp; SV* sv; if (n < 0) n = 0; if (size == 0 || n == 0) { res = newAV(); } else { res_size = size * n; if (res_size / size != n) {/* check for overflow */ ENTER_PYTHON; return PyErr_NoMemory(); } res = newAV(); av_extend(res, res_size-1); for (i = 0; i < size; i++) { SV** svp = av_fetch(av, i, 0); if (svp) { for (j = 0; j < n; j++) { sv = newSVsv(*svp); if (!av_store(res, i + j*size, sv)) SvREFCNT_dec(sv); } } } } sv = newRV_noinc((SV*)res); ENTER_PYTHON; PERL_LOCK; pyo_res = PySVRV_New(sv); SvREFCNT_dec(sv); /* since PySVRV_New incremented it */ PERL_UNLOCK; } else { ENTER_PYTHON; type_error("Can't repeat", sv); pyo_res = NULL; } ASSERT_LOCK_PYTHON; return pyo_res; } static PyObject * pysvrv_slice(PySVRV *self, int ilow, int ihigh) { SV* sv; PyObject *pyo_res; dCTXP; ASSERT_LOCK_PYTHON; CHECK_OWNED_PY; ENTER_PERL; SET_CUR_PERL; sv = SvRV(self->rv); if (SvTYPE(sv) == SVt_PVAV) { AV* av = (AV*)sv; I32 size = av_len(av)+1; AV* res; I32 i; SV** svp; SV* sv; if (ilow < 0) ilow = 0; if (ihigh > size) ihigh = size; if (ihigh < ilow) ihigh = ilow; res = newAV(); if (ihigh != ilow) av_extend(av, ihigh - ilow - 1); for (i = ilow; i < ihigh; i++) { svp = av_fetch(av, i, 0); if (svp) { sv = newSVsv(*svp); if (!av_store(res, i-ilow, sv)) SvREFCNT_dec(sv); } else if (i == ihigh - 1) { /* in order to get the perl array to get the right length * we need to to special case the last element. */ sv = newSV(0); if (!av_store(res, i-ilow, sv)) SvREFCNT_dec(sv); } } sv = newRV_noinc((SV*)res); ENTER_PYTHON; PERL_LOCK; pyo_res = PySVRV_New(sv); SvREFCNT_dec(sv); /* since PySVRV_New incremented it */ PERL_UNLOCK; } else { ENTER_PYTHON; type_error("Can't slice", sv); pyo_res = NULL; } ASSERT_LOCK_PYTHON; return pyo_res; } static int pysvrv_ass_slice(PySVRV *self, int ilow, int ihigh, PyObject *v) { SV* sv; int status; /* return value */ dCTXP; CHECK_OWNED_INT; ASSERT_LOCK_PYTHON; ENTER_PERL; SET_CUR_PERL; sv = SvRV(self->rv); if (SvTYPE(sv) == SVt_PVAV) { AV* av = (AV*)sv; I32 size = av_len(av)+1; int n; AV* av2; SV** svp; if (v == NULL) n = 0; else if (PySVRV_Check(v) && SvTYPE(SvRV(((PySVRV *)v)->rv)) == SVt_PVAV) { #ifdef MULTI_PERL if (!owned_by((PySVRV*)v, ctx->perl)) { ENTER_PYTHON; goto FAIL; } #endif av2 = (AV*)SvRV(((PySVRV *)v)->rv); n = av_len(av2)+1; } else { ENTER_PYTHON; PyErr_SetString(PyExc_TypeError, "Slice assignment type mismatch"); goto FAIL; } if (ilow < 0) ilow = 0; if (ihigh > size) ihigh = size; if (ihigh < ilow) ihigh = ilow; /* printf("slice assign(%d:%d, %d)\n", ilow, ihigh, n); */ if (array_splice(av, ilow, ihigh-ilow, n) == -1) goto FAIL; /* Copy elements from av2 */ while (n) { n--; svp = av_fetch(av2, n, 0); if (svp) { SV* sv = newSVsv(*svp); if (!av_store(av, ilow+n, sv)) { /* XXX might be to late to throw an exception :-( */ SvREFCNT_dec(sv); } } } ENTER_PYTHON; status = 0; } else { ENTER_PYTHON; type_error("Can't slice", sv); FAIL: status = -1; } ASSERT_LOCK_PYTHON; return status; } static PyNumberMethods pysvrv_as_number = { 0, /*nb_add*/ 0, /*nb_subtract*/ 0, /*nb_multiply*/ 0, /*nb_divide*/ 0, /*nb_remainder*/ 0, /*nb_divmod*/ 0, /*nb_power*/ 0, /*nb_negative*/ 0, /*nb_positive*/ 0, /*nb_absolute*/ (inquiry)pysvrv_nonzero, /*nb_nonzero*/ 0, /*nb_invert*/ 0, /*nb_lshift*/ 0, /*nb_rshift*/ 0, /*nb_and*/ 0, /*nb_xor*/ 0, /*nb_or*/ 0, /*nb_coerce*/ 0, /*nb_int*/ 0, /*nb_long*/ 0, /*nb_float*/ 0, /*nb_oct*/ 0, /*nb_hex*/ }; static PyMappingMethods pysvrv_as_mapping = { (inquiry)pysvrv_length, /* mp_length */ (binaryfunc)pysvrv_subscript, /* mp_subscript */ (objobjargproc)pysvrv_ass_sub, /* mp_ass_subscript */ }; static PySequenceMethods pysvrv_as_sequence = { (inquiry)pysvrv_length, /*sq_length*/ (binaryfunc)pysvrv_concat, /*sq_concat*/ (intargfunc)pysvrv_repeat, /*sq_repeat*/ (intargfunc)pysvrv_item, /*sq_item*/ (intintargfunc)pysvrv_slice, /*sq_slice*/ 0, /*sq_ass_item*/ (intintobjargproc)pysvrv_ass_slice, /*sq_ass_slice*/ #if PY_MAJOR_VERSION >= 1 && PY_MINOR_VERSION >= 6 0, /*sq_contains*/ #endif }; //XXX must compile as a C++ file on Windows PyTypeObject SVRVtype = { PyObject_HEAD_INIT(&PyType_Type) 0, /* Number of items for varobject */ "perl ref", /* Name of this type */ sizeof(PyTypeObject), /* Basic object size */ 0, /* Item size for varobject */ (destructor)pysvrv_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ (getattrfunc)pysvrv_getattr, /*tp_getattr*/ (setattrfunc)pysvrv_setattr, /*tp_setattr*/ 0, /*tp_compare*/ (reprfunc)pysvrv_repr, /*tp_repr*/ &pysvrv_as_number, /*tp_as_number*/ &pysvrv_as_sequence, /*tp_as_sequence*/ &pysvrv_as_mapping, /*tp_as_mapping*/ 0, /*tp_hash*/ (ternaryfunc)pysvrv_call, /*tp_call*/ }; #ifdef __cplusplus } #endif