util.c (mkexc): Populate dictionary before constructing exception class.
[pyke] / util.c
diff --git a/util.c b/util.c
index 27af83a..29f7d12 100644 (file)
--- a/util.c
+++ b/util.c
@@ -1,13 +1,11 @@
 /* -*-c-*-
  *
- * $Id$
- *
  * Miscellaneous utilities (not Catacomb-specific)
  *
  * (c) 2005 Straylight/Edgeware
  */
 
-/*----- Licensing notice --------------------------------------------------* 
+/*----- Licensing notice --------------------------------------------------*
  *
  * This file is part of the Python interface to Catacomb.
  *
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
- * 
+ *
  * Catacomb/Python is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU General Public License
  * along with Catacomb/Python; if not, write to the Free Software Foundation,
  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
 #include "catacomb-python.h"
 
+/* #undef HAVE_LONG_LONG */
+
+/*----- External values ---------------------------------------------------*/
+
+static PyObject *modname = 0;
+PyObject *home_module = 0;
+
 /*----- Conversions -------------------------------------------------------*/
 
-#define GETU_(n)                                                       \
-  PyObject *getu##n(uint##n w)                                         \
-  {                                                                    \
-    if (w <= MASK##n)                                                  \
-      return (PyInt_FromLong(w));                                      \
-    else                                                               \
-      return (PyLong_FromUnsignedLong(w));                             \
-  }
-DOUINTSZ(GETU_)
+PyObject *getulong(unsigned long w)
+{
+  if (w <= LONG_MAX)
+    return (PyInt_FromLong(w));
+  else
+    return (PyLong_FromUnsignedLong(w));
+}
+
+#ifndef HAVE_LONG_LONG
+static PyObject *i32 = 0;
+static int init_i32(void)
+  { if (!i32 && (i32 = PyInt_FromLong(32)) == 0) return (-1); return (0); }
+#endif
+
+PyObject *getk64(kludge64 u)
+{
+#ifdef HAVE_LONG_LONG
+  return (PyLong_FromUnsignedLongLong(GET64(unsigned PY_LONG_LONG, u)));
+#else
+  PyObject *i = 0, *j = 0, *t;
+  PyObject *rc = 0;
+
+  if (init_i32()) goto end;
+  if ((i = PyLong_FromUnsignedLong(HI64(u))) == 0) goto end;
+  if ((t = PyNumber_InPlaceLshift(i, i32)) == 0) goto end;
+  Py_DECREF(i); i = t;
+  if ((j = PyLong_FromUnsignedLong(LO64(u))) == 0) goto end;
+  if ((t = PyNumber_InPlaceOr(i, j)) == 0) goto end;
+  Py_DECREF(i); i = t;
+  if ((rc = PyNumber_Int(i)) == 0) goto end;
+end:
+  if (i) Py_DECREF(i);
+  if (j) Py_DECREF(j);
+  return (rc);
+#endif
+}
 
 PyObject *getbool(int b)
 {
@@ -60,6 +92,7 @@ int convulong(PyObject *o, void *pp)
   unsigned long *p = pp;
   PyObject *t;
 
+  if (!o) VALERR("can't delete");
   if (PyInt_Check(o)) {
     i = PyInt_AS_LONG(o);
     if (i < 0) VALERR("must be nonnegative");
@@ -75,15 +108,32 @@ end:
   return (0);
 }
 
+#ifdef HAVE_UINT64
+#  define CONVu64(n) do {                                              \
+     kludge64 k;                                                       \
+     uint64 t;                                                         \
+     if (!convk64(o, &k)) goto end;                                    \
+     t = GET64(uint64, k);                                             \
+     if (t > MASK##n) VALERR("out of range");                          \
+     *p = t;                                                           \
+   } while (0)
+#else
+#  define CONVu64(n) assert(!"shouldn't be possible")
+#endif
+
 #define CONVU_(n)                                                      \
   int convu##n(PyObject *o, void *pp)                                  \
   {                                                                    \
     unsigned long u;                                                   \
     uint##n *p = pp;                                                   \
                                                                        \
-    if (!convulong(o, &u)) goto end;                                   \
-    if (u > MASK##n) VALERR("out of range");                           \
-    *p = u;                                                            \
+    if (MASK##n > ULONG_MAX)                                           \
+      CONVu64(n);                                                      \
+    else {                                                             \
+      if (!convulong(o, &u)) goto end;                                 \
+      if (u > MASK##n) VALERR("out of range");                         \
+      *p = u;                                                          \
+    }                                                                  \
     return (1);                                                                \
   end:                                                                 \
     return (0);                                                                \
@@ -103,6 +153,42 @@ end:
   return (0);
 }
 
+int convk64(PyObject *o, void *pp)
+{
+  PyObject *i = 0;
+  int rc = 0;
+#if HAVE_LONG_LONG
+  unsigned PY_LONG_LONG t;
+#else
+  PyObject *t;
+  uint32 lo, hi;
+#endif
+
+  if (!o) VALERR("can't delete");
+#if HAVE_LONG_LONG
+  if ((i = PyNumber_Long(o)) == 0) goto end;
+  t = PyLong_AsUnsignedLongLong(i);
+  if (t == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred()) goto end;
+  ASSIGN64(*(kludge64 *)pp, t);
+#else
+  if (init_i32()) goto end;
+  if ((i = PyNumber_Int(o)) == 0) goto end;
+  lo = PyInt_AsUnsignedLongMask(i);
+  if ((t = PyNumber_InPlaceRshift(i, i32)) == 0) goto end;
+  Py_DECREF(i); i = t;
+  hi = PyInt_AsUnsignedLongMask(i);
+  if ((t = PyNumber_InPlaceRshift(i, i32)) == 0) goto end;
+  Py_DECREF(i); i = t;
+  if (PyObject_IsTrue(i)) VALERR("out of range");
+  SET64(*(kludge64 *)pp, hi, lo);
+#endif
+  rc = 1;
+
+end:
+  if (i) Py_DECREF(i);
+  return (rc);
+}
+
 int convmpw(PyObject *o, void *pp)
 {
   unsigned long u;
@@ -131,8 +217,11 @@ end:
 
 int convbool(PyObject *o, void *pp)
 {
+  if (!o) VALERR("can't delete");
   *(int *)pp = PyObject_IsTrue(o);
   return (1);
+end:
+  return (0);
 }
 
 /*----- Type messing ------------------------------------------------------*/
@@ -147,13 +236,13 @@ void *newtype(PyTypeObject *metaty,
     (PyHeapTypeObject *)_PyObject_GC_Malloc(_PyObject_VAR_SIZE(metaty, 0));
   if (!skel) skel = &emptytype;
   memcpy(ty, skel, sizeof(*skel));
-  if (ty->type.tp_base) Py_INCREF(ty->type.tp_base);
+  if (ty->ht_type.tp_base) Py_INCREF(ty->ht_type.tp_base);
 #define COPY(blah) do {                                                        \
-    if (ty->type.tp_as_##blah) {                                       \
+    if (ty->ht_type.tp_as_##blah) {                                    \
       memcpy(&ty->as_##blah,                                           \
-            ty->type.tp_as_##blah,                                     \
+            ty->ht_type.tp_as_##blah,                                  \
             sizeof(ty->as_##blah));                                    \
-      ty->type.tp_as_##blah = &ty->as_##blah;                          \
+      ty->ht_type.tp_as_##blah = &ty->as_##blah;                       \
     }                                                                  \
   } while (0)
   COPY(number);
@@ -162,21 +251,27 @@ void *newtype(PyTypeObject *metaty,
   COPY(buffer);
 #undef COPY
   if (name)
-    ty->name = PyString_FromString(name);
-  else if (ty->type.tp_name)
-    ty->name = PyString_FromString(ty->type.tp_name);
-  if (ty->name)
-    ty->type.tp_name = PyString_AS_STRING(ty->name);
-  PyObject_INIT(&ty->type, metaty);
+    ty->ht_name = PyString_FromString(name);
+  else if (ty->ht_type.tp_name)
+    ty->ht_name = PyString_FromString(ty->ht_type.tp_name);
+  if (ty->ht_name)
+    ty->ht_type.tp_name = PyString_AS_STRING(ty->ht_name);
+  DISCARD(PyObject_INIT(&ty->ht_type, metaty));
   Py_INCREF(metaty);
   return (ty);
 }
 
-PyTypeObject *inittype(PyTypeObject *tyskel)
+void typeready(PyTypeObject *ty)
 {
-  PyTypeObject *ty = newtype(&PyType_Type, tyskel, 0);
-  ty->tp_flags |= Py_TPFLAGS_HEAPTYPE;
   PyType_Ready(ty);
+  PyDict_SetItemString(ty->tp_dict, "__module__", modname);
+}
+
+PyTypeObject *inittype(PyTypeObject *tyskel, PyTypeObject *meta)
+{
+  PyTypeObject *ty = newtype(meta, tyskel, 0);
+  ty->tp_flags |= Py_TPFLAGS_HEAPTYPE;
+  typeready(ty);
   return (ty);
 }
 
@@ -185,14 +280,14 @@ PyTypeObject *inittype(PyTypeObject *tyskel)
 void setconstants(PyObject *mod, const struct nameval *c)
 {
   PyObject *x;
+  unsigned long u;
 
   while (c->name) {
-    if (c->value > LONG_MAX)
-      x = PyLong_FromUnsignedLong(c->value);
-    else
-      x = PyInt_FromLong(c->value);
-    PyModule_AddObject(mod, (/*unconst*/ char *)c->name, x);
-    c++;
+    u = c->value;
+    if (u <= LONG_MAX) x = PyInt_FromLong(u);
+    else if (c->f&CF_SIGNED) x = PyInt_FromLong(-1 - (long)(ULONG_MAX - u));
+    else x = PyLong_FromUnsignedLong(u);
+    PyModule_AddObject(mod, (/*unconst*/ char *)c->name, x); c++;
   }
 }
 
@@ -220,8 +315,8 @@ PyMethodDef *donemethods(void)
 
 /*----- Exceptions --------------------------------------------------------*/
 
-PyObject * mkexc(PyObject *mod, PyObject *base,
-                const char *name, PyMethodDef *mm)
+PyObject *mkexc(PyObject *mod, PyObject *base,
+               const char *name, PyMethodDef *mm)
 {
   PyObject *nameobj = 0;
   PyObject *dict = 0;
@@ -229,13 +324,7 @@ PyObject * mkexc(PyObject *mod, PyObject *base,
   PyObject *func = 0;
   PyObject *meth = 0;
 
-  if ((nameobj = PyString_FromFormat("%s.%s",
-                                    PyModule_GetName(mod),
-                                    name)) == 0 ||
-      (dict = PyDict_New()) == 0 ||
-      (exc = PyErr_NewException(PyString_AS_STRING(nameobj),
-                               base, dict)) == 0)
-    goto fail;
+  if ((dict = PyDict_New()) == 0) goto fail;
 
   if (mm) {
     while (mm->ml_name) {
@@ -249,6 +338,13 @@ PyObject * mkexc(PyObject *mod, PyObject *base,
     }
   }
 
+  if ((nameobj = PyString_FromFormat("%s.%s",
+                                    PyModule_GetName(mod),
+                                    name)) == 0 ||
+      (exc = PyErr_NewException(PyString_AS_STRING(nameobj),
+                               base, dict)) == 0)
+    goto fail;
+
 done:
   Py_XDECREF(nameobj);
   Py_XDECREF(dict);
@@ -262,6 +358,101 @@ fail:
   goto done;
 }
 
+void report_lost_exception_v(struct excinfo *exc,
+                            const char *why, va_list ap)
+{
+  PyObject *hookfn = 0;
+  PyObject *whyobj = 0;
+  PyObject *obj = 0;
+
+  /* Make sure we start out without a pending exception, or this will get
+   * really confusing.
+   */
+  assert(!PyErr_Occurred());
+
+  /* Format the explanation. */
+  if (why) whyobj = PyString_FromFormatV(why, ap);
+  else { whyobj = Py_None; Py_INCREF(whyobj); }
+
+  /* Find our home module's `lostexchook' function.  This won't work if
+   * there's no module, or the function isn't defined, or it's `None'.
+   */
+  if (!home_module) goto sys;
+  hookfn = PyObject_GetAttrString(home_module, "lostexchook");
+  if (hookfn == Py_None) goto sys;
+  else if (hookfn) ;
+  else if (!PyErr_ExceptionMatches(PyExc_AttributeError)) goto ouch;
+  else { PyErr_Clear(); goto sys; }
+
+  /* Call the hook function. */
+  obj = PyObject_CallFunction(hookfn, "(OOOO)",
+                             whyobj, exc->ty, exc->val, exc->tb);
+  if (!obj) goto ouch;
+  goto end;
+
+  /* Something went wrong reporting the problem. */
+ouch:
+  PySys_WriteStderr("\n!!! FAILURE REPORTING LOST EXCEPTION\n");
+  PyErr_Print();
+  /* drop through... */
+
+  /* There was no hook, so try to do something sensible using
+   * `sys.excepthook'.
+   */
+sys:
+  PySys_WriteStderr("\n!!! LOST EXCEPTION: %s\n",
+                   PyString_AS_STRING(whyobj));
+  RESTORE_EXCINFO(exc);
+  PyErr_Print();
+  /* drop through... */
+
+  /* Clean up afterwards. */
+end:
+  Py_XDECREF(hookfn);
+  Py_XDECREF(whyobj);
+  Py_XDECREF(obj);
+}
+
+void report_lost_exception(struct excinfo *exc, const char *why, ...)
+{
+  va_list ap;
+
+  va_start(ap, why);
+  report_lost_exception_v(exc, why, ap);
+  va_end(ap);
+}
+
+void stash_exception(struct excinfo *exc, const char *why, ...)
+{
+  va_list ap;
+  struct excinfo stash;
+
+  if (!exc->ty)
+    STASH_EXCINFO(exc);
+  else {
+    va_start(ap, why);
+    STASH_EXCINFO(&stash);
+    report_lost_exception_v(&stash, why, ap);
+    va_end(ap);
+  }
+}
+
+void restore_exception(struct excinfo *exc, const char *why, ...)
+{
+  va_list ap;
+  struct excinfo stash;
+
+  if (!PyErr_Occurred())
+    RESTORE_EXCINFO(exc);
+  else {
+    va_start(ap, why);
+    STASH_EXCINFO(&stash);
+    report_lost_exception_v(exc, why, ap);
+    RESTORE_EXCINFO(&stash);
+    va_end(ap);
+  }
+}
+
 /*----- Generic dictionary methods ----------------------------------------*/
 
 static PyTypeObject *itemiter_pytype, *valiter_pytype;
@@ -280,7 +471,7 @@ static void iter_pydealloc(PyObject *me)
 static PyObject *itemiter_pynext(PyObject *me)
 {
   PyObject *k = 0, *v = 0, *rc = 0;
-  
+
   if ((k = PyIter_Next(ITER_I(me))) != 0 &&
       (v = PyObject_GetItem(ITER_MAP(me), k)) != 0)
     rc = Py_BuildValue("(OO)", k, v);
@@ -313,7 +504,7 @@ static PyTypeObject itemiter_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Iterates over the items of a mapping.",
+"Iterates over the keys of a mapping.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -339,7 +530,7 @@ static PyTypeObject itemiter_pytype_skel = {
 static PyObject *valiter_pynext(PyObject *me)
 {
   PyObject *k = 0, *rc = 0;
-  
+
   if ((k = PyIter_Next(ITER_I(me))) != 0)
     rc = PyObject_GetItem(ITER_MAP(me), k);
   Py_XDECREF(k);
@@ -371,7 +562,7 @@ static PyTypeObject valiter_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Iterates over the items of a mapping.",
+"Iterates over the values of a mapping.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -405,13 +596,12 @@ PySequenceMethods gmap_pysequence = {
   PyMapping_HasKey,                    /* @sq_contains@ */
   0,                                   /* @sq_inplace_concat@ */
   0                                    /* @sq_inplace_repeat@ */
-};  
+};
 
-int gmap_pysize(PyObject *me)
+Py_ssize_t gmap_pysize(PyObject *me)
 {
   PyObject *i = 0, *x = 0;
-  int rc = -1;
-  int n = 0;
+  Py_ssize_t rc = -1, n = 0;
 
   if ((i = PyObject_GetIter(me)) == 0) goto done;
   while ((x = PyIter_Next(i)) != 0) { n++; Py_DECREF(x); x = 0; }
@@ -456,7 +646,7 @@ PyObject *gmapmeth_values(PyObject *me, PyObject *arg)
       (l = PyList_New(0)) == 0 ||
       (i = PyObject_GetIter(me)) == 0)
     goto done;
-  while ((k = PyIter_Next(i)) != 0) {  
+  while ((k = PyIter_Next(i)) != 0) {
     if ((v = PyObject_GetItem(me, k)) == 0 ||
        PyList_Append(l, v))
       err = -1;
@@ -553,7 +743,7 @@ PyObject *gmapmeth_get(PyObject *me, PyObject *arg, PyObject *kw)
 {
   PyObject *k, *def = Py_None, *v;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO:get", def_kwlist, &k, &def))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:get", def_kwlist, &k, &def))
     return (0);
   if ((v = PyObject_GetItem(me, k)) != 0) return (v);
   PyErr_Clear();
@@ -564,7 +754,7 @@ PyObject *gmapmeth_setdefault(PyObject *me, PyObject *arg, PyObject *kw)
 {
   PyObject *k, *def = Py_None, *v;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO:setdefault",
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:setdefault",
                                   def_kwlist, &k, &def))
     return (0);
   if ((v = PyObject_GetItem(me, k)) != 0) return (v);
@@ -577,14 +767,16 @@ PyObject *gmapmeth_pop(PyObject *me, PyObject *arg, PyObject *kw)
 {
   PyObject *k, *def = 0, *v;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO:pop", def_kwlist, &k, &def))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:pop", def_kwlist, &k, &def))
     return (0);
   if ((v = PyObject_GetItem(me, k)) != 0) {
     PyObject_DelItem(me, k);
     return (v);
-  }
-  PyErr_Clear();
-  RETURN_OBJ(def);
+  } else if (def) {
+    PyErr_Clear();
+    RETURN_OBJ(def);
+  } else
+    return (0);
 }
 
 PyObject *gmapmeth_update(PyObject *me, PyObject *arg)
@@ -614,7 +806,7 @@ PyObject *gmapmeth_popitem(PyObject *me, PyObject *arg)
   PyObject *i = 0, *k = 0, *v = 0, *rc = 0;
 
   if (!PyArg_ParseTuple(arg, ":popitem") ||
-      (i = PyObject_GetIter(me)))
+      (i = PyObject_GetIter(me)) == 0)
     goto end;
   if ((k = PyIter_Next(i)) == 0) {
     if (!PyErr_Occurred()) VALERR("popitem(): mapping is empty");
@@ -636,13 +828,32 @@ PyMethodDef gmap_pymethods[] = {
 
 /*----- Initialization ----------------------------------------------------*/
 
-void util_init(void)
+static PyObject *meth__set_home_module(PyObject *me, PyObject *arg)
+{
+  PyObject *mod;
+
+  if (!PyArg_ParseTuple(arg, "O!:_set_home_module", &PyModule_Type, &mod))
+    return (0);
+  Py_XDECREF(home_module); home_module = mod; Py_INCREF(home_module);
+  RETURN_NONE;
+}
+
+static const PyMethodDef methods[] = {
+#define METHNAME(func) meth_##func
+  METH (_set_home_module,      "_set_home_module(MOD)")
+#undef METHNAME
+  { 0 }
+};
+
+void util_pyinit(void)
 {
+  modname = PyString_FromString("catacomb");
   INITTYPE(itemiter, root);
   INITTYPE(valiter, root);
+  addmethods(methods);
 }
 
-void util_insert(PyObject *mod)
+void util_pyinsert(PyObject *mod)
 {
   INSERT("ItemIter", itemiter_pytype);
   INSERT("ValueIter", valiter_pytype);