Added code in js_gcroot to register and reverse-resolve arbitrary pointers
authorMatthew Mondor <mmondor@pulsar-zone.net>
Wed, 18 Oct 2006 05:07:48 +0000 (05:07 +0000)
committerMatthew Mondor <mmondor@pulsar-zone.net>
Wed, 18 Oct 2006 05:07:48 +0000 (05:07 +0000)
to their associated JSObject *, JSContext * and arbitrary udata void *.
Currently used by js_pgsql's notice processing facilities.

mmsoftware/js/classes/js_gcroot.c
mmsoftware/js/classes/js_gcroot.h
mmsoftware/js/classes/js_pgsql.c
mmsoftware/js/js-appserv/src/js-appserv.c
mmsoftware/js/js-sh/src/GNUmakefile
mmsoftware/js/js-sh/src/js-sh.c

index fe99071..194d550 100644 (file)
@@ -1,10 +1,48 @@
-/* $Id: js_gcroot.c,v 1.2 2006/10/05 18:43:30 mmondor Exp $ */
+/* $Id: js_gcroot.c,v 1.3 2006/10/18 05:07:42 mmondor Exp $ */
 
 /*
  * Copyright (c) 2006, Matthew Mondor
  * ALL RIGHTS RESERVED.
  */
 
+/*
+ * This module provides APIs to several tricks which may be required to
+ * support the functionality of a few C libraries when writing their JS stubs.
+ * Here are cases where it can be useful:
+ *
+ * - An object must be provided to a C library for an arbitrary amount of time
+ *   and we must ensure that it does not get freed by the potential
+ *   auto-destruction of the object by the garbage collector.
+ *   An example of this is in js_pgsql.c where PQtrace() can be provided a
+ *   FILE *.  We thus can store the wrapping File object as a property into
+ *   the context-global rooted GCRoot object, and delete it from the object
+ *   when PQuntrace() is called.
+ *   An application using this functionality must call js_InitGCRoot() when
+ *   creating contexts and js_DestroyGCRoot() before destroying the contexts.
+ *   It may then call js_GCRoot() to obtain the JSObject pointer of the rooted
+ *   object to assiciate properties with.  Since this clobbers the private
+ *   data which can be set for a context using JS_SetContextPrivate(),
+ *   alternate functions are provided to store arbitrary context-specific
+ *   data: js_GCRoot_udata_set()/js_GCRoot_udata_get().
+ * - A C API function must be provided an object which is internally wrapped
+ *   by a JSObject, but to which it is impossibe to pass the JSContext * and
+ *   JSObject *.  There must then be a way to map internal arbitrary C
+ *   pointers to their corresponding JSContext/JSObject pointers, in which
+ *   case we may use the js_map_add() function at object construction and
+ *   js_map_remove() at its destruction, and js_map_lookup() to obtain the
+ *   necessary information from within the C function.  This case most often
+ *   occurs for callback functions, like for the notice receiver callback
+ *   in js_pgsql.c.
+ *   To use this functionality, the application must call js_map_init() after
+ *   creating the runtime, and js_map_destroy() before destroying the runtime.
+ *   Provision is also made to store an additional user data pointer.
+ * - In cases where the private data set on an object also must hold the
+ *   JSContext * and JSObject * associated with it, it is possible to use
+ *   the js_udata_alloc() and js_udata_free() to create/destroy private
+ *   data objects.  Additionally, udata_t also allows to store more than one
+ *   void *, which may be useful.
+ */
+
 
 
 #include <sys/types.h>
  */
 typedef struct cxspec {
        JSObject        *gcroot;
-       void            *udata;
+       udata_t         *udata;
 } cxspec_t;
 
 
 
 /*
+ * Static functions prototypes
+ */
+static int             omap_keycmp(const void *, const void *, size_t);
+static u_int32_t       omap_keyhash(const void *, size_t);
+
+
+
+/*
  * Static globals
  */
 
@@ -47,6 +93,14 @@ static JSClass gcroot_class = {
        JS_FinalizeStub
 };
 
+/*
+ * XXX since these are runtime-global, they should be lock-protected if we
+ * wanted thread-safe code.
+ */
+static pool_t          udata_pool;
+static pool_t          omap_pool;
+static hashtable_t     omap_table;
+
 
 
 /*
@@ -91,6 +145,7 @@ js_DestroyGCRoot(JSContext *cx)
        cxspec_t        *d;
 
        if ((d = JS_GetContextPrivate(cx)) != NULL) {
+               assert(d->udata == NULL);
                if (d->gcroot != NULL) {
                        if (!JS_RemoveRoot(cx, &d->gcroot))
                                (void) fprintf(stderr,
@@ -113,7 +168,7 @@ js_GCRoot(JSContext *cx)
 }
 
 void
-js_GCRoot_udata_set(JSContext *cx, void *udata)
+js_GCRoot_udata_set(JSContext *cx, udata_t *udata)
 {
        cxspec_t        *d;
 
@@ -123,8 +178,8 @@ js_GCRoot_udata_set(JSContext *cx, void *udata)
        d->udata = udata;
 }
 
-void *
-js_GCroot_udata_get(JSContext *cx)
+udata_t *
+js_GCRoot_udata_get(JSContext *cx)
 {
        cxspec_t        *d;
 
@@ -133,3 +188,124 @@ js_GCroot_udata_get(JSContext *cx)
 
        return d->udata;
 }
+
+udata_t *
+js_udata_alloc(JSContext *cx, JSObject *obj)
+{
+       udata_t *udata;
+
+       if ((udata = (udata_t *)pool_alloc(&udata_pool, FALSE)) != NULL) {
+               int     i;
+
+               udata->cx = cx;
+               udata->obj = obj;
+               for (i = 0; i < UDATA_MAX; )
+                       udata->udata[i++] = NULL;
+       }
+
+       return udata;
+}
+
+void
+js_udata_free(udata_t *udata)
+{
+
+       (void) pool_free((pnode_t *)udata);
+}
+
+
+JSBool
+js_map_init(void)
+{
+
+       if (!pool_init(&udata_pool, "udata_pool", malloc, free, NULL, NULL,
+           sizeof(udata_t), 64, 1, 0))
+               goto err;
+
+       if (!pool_init(&omap_pool, "omap_pool", malloc, free, NULL, NULL,
+           sizeof(omap_t), 64, 1, 0))
+               goto err;
+
+       if (!hashtable_init(&omap_table, "omap_table", HT_DEFAULT_CAPACITY,
+           HT_DEFAULT_FACTOR, malloc, free, omap_keycmp, omap_keyhash, TRUE))
+               goto err;
+
+       return JS_TRUE;
+
+err:
+       if (HASHTABLE_VALID(&omap_table))
+               hashtable_destroy(&omap_table, FALSE);
+       if (POOL_VALID(&omap_pool))
+               (void) pool_destroy(&omap_pool);
+       if (POOL_VALID(&udata_pool))
+               (void) pool_destroy(&udata_pool);
+
+       return JS_FALSE;
+}
+
+void
+js_map_destroy(void)
+{
+
+       if (HASHTABLE_VALID(&omap_table))
+               hashtable_destroy(&omap_table, FALSE);
+       if (POOL_VALID(&omap_pool))
+               (void) pool_destroy(&omap_pool);
+       if (POOL_VALID(&udata_pool))
+               (void) pool_destroy(&udata_pool);
+}
+
+JSBool
+js_map_add(void *o, JSContext *cx, JSObject *obj, void *udata)
+{
+       omap_t  *omap;
+
+       assert(cx != NULL && obj != NULL && o != NULL);
+       if ((omap = (omap_t *)pool_alloc(&omap_pool, FALSE)) == NULL)
+               return FALSE;
+
+       omap->mem = o;
+       omap->cx = cx;
+       omap->obj = obj;
+       omap->udata = udata;
+
+       if (!hashtable_link(&omap_table, &omap->node, &omap->mem,
+           sizeof(void *), TRUE)) {
+               (void) pool_free((pnode_t *)omap);
+               return JS_FALSE;
+       }
+
+       return JS_TRUE;
+}
+
+omap_t *
+js_map_lookup(void *o)
+{
+
+       return (omap_t *)hashtable_lookup(&omap_table, &o, sizeof(void *));
+}
+
+void
+js_map_remove(omap_t *omap)
+{
+
+       hashtable_unlink(&omap_table, &omap->node);
+       (void) pool_free((pnode_t *)omap);
+}
+
+static int
+omap_keycmp(const void *a, const void *b, size_t s)
+{
+       long    da = (long)*(long *)a;
+       long    db = (long)*(long *)b;
+
+       return (int)(da - db);
+}
+
+static u_int32_t
+omap_keyhash(const void *a, size_t s)
+{
+       long    d = (long)*(long *)a;
+
+       return (u_int32_t)(d & 0xffffffff);
+}
index c52ba44..c941601 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: js_gcroot.h,v 1.2 2006/10/05 18:43:30 mmondor Exp $ */
+/* $Id: js_gcroot.h,v 1.3 2006/10/18 05:07:42 mmondor Exp $ */
 
 /*
  * Copyright (c) 2006, Matthew Mondor
@@ -8,12 +8,65 @@
 #ifndef JSGCROOT_H
 #define JSGCROOT_H
 
-#include <js_gcroot.h>
+
+
+#include <jsapi.h>
+
+#include <mmlist.h>
+#include <mmpool.h>
+#include <mmhash.h>
+
+
+
+#define UDATA_MAX      4
+
+/*
+ * This structure allows to store/retreive user data from an object or
+ * context, in a way that passing the udata_t * alone permits to retreive the
+ * JSContext *, JSObject * and room for various user data pointers.
+ * This is useful when implementing various classes.
+ * We are using a pool_t in order to efficiently allocate and free these.
+ */
+typedef struct udata {
+       pnode_t         node;
+       JSContext       *cx;
+       JSObject        *obj;
+       void            *udata[UDATA_MAX];
+} udata_t;
+
+/*
+ * Used to implement a lookup hash table mapping arbitrary memory addresses to
+ * their JSObject *.  This ideally would be on a per-context basis, but since
+ * we must be able to only rely on the address, we must make this table
+ * process-global.  In fact, we also need to obtain the JSContext pointer.
+ * Thus, js_map_init() and js_map_destroy() should be called after runtime
+ * creation and before runtime finalization, respectively.
+ */
+typedef struct omap {
+       hashnode_t      node;
+       void            *mem;   /* Key */
+       JSContext       *cx;
+       JSObject        *obj;
+       void            *udata;
+} omap_t;
+
+
 
 extern JSBool  js_InitGCRoot(JSContext *);
 extern void    js_DestroyGCRoot(JSContext *);
 extern JSObject        *js_GCRoot(JSContext *);
-extern void    js_GCRoot_udata_set(JSContext *, void *);
-extern void    *js_GCRoot_udata_get(JSContext *);
+extern void    js_GCRoot_udata_set(JSContext *, udata_t *);
+extern udata_t *js_GCRoot_udata_get(JSContext *);
+
+extern udata_t *js_udata_alloc(JSContext *, JSObject *);
+extern void    js_udata_free(udata_t *);
+
+extern JSBool  js_map_init(void);
+extern void    js_map_destroy(void);
+extern JSBool  js_map_add(void *, JSContext *, JSObject *, void *);
+extern omap_t  *js_map_lookup(void *);
+extern void    js_map_remove(omap_t *);
+
+
 
 #endif
index a6c6863..e88a435 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: js_pgsql.c,v 1.9 2006/10/05 18:43:30 mmondor Exp $ */
+/* $Id: js_pgsql.c,v 1.10 2006/10/18 05:07:42 mmondor Exp $ */
 
 /*
  * Copyright (c) 2006, Matthew Mondor
@@ -17,9 +17,6 @@
  *   generated objects instances's private data (using structures as necessary
  *   instead of simply wrapping around the native object's pointer
  *   (actually PGconn object).
- * - 28.10. Notice Processing XXX
- *   Either place one(s) that use syslog(3) transparently, or somehow allow
- *   the user to set a custom handler
  * - Large objects API
  * - Compare to PHP library to verify if missing any nice functions ideas
  * - See what to do about the following functions:
@@ -198,6 +195,12 @@ static JSBool      pgconn_m_PQputCopyEnd(JSContext *, JSObject *, uintN, jsval *,
                    jsval *);
 static JSBool  pgconn_m_PQgetCopyData(JSContext *, JSObject *, uintN, jsval *,
                    jsval *);
+static JSBool  pgconn_m_PQsetNoticeReceiver(JSContext *, JSObject *, uintN,
+                   jsval *, jsval *);
+static void    notice_receiver(void *, const PGresult *);
+static JSBool  pgconn_m_PQsetNoticeProcessor(JSContext *, JSObject *, uintN,
+                   jsval *, jsval *);
+static void    notice_processor(void *, const char *);
 
 static JSObject        *js_InitPGresultClass(JSContext *, JSObject *);
 static JSBool  pgresult_constructor(JSContext *, JSObject *, uintN, jsval *,
@@ -409,6 +412,8 @@ static JSFunctionSpec pgconn_methods[] = {
        { "putCopyData", pgconn_m_PQputCopyData, 1, 0, 0 },
        { "putCopyEnd", pgconn_m_PQputCopyEnd, 1, 0, 0 },
        { "getCopyData", pgconn_m_PQgetCopyData, 1, 0, 0 },
+       { "setNoticeReceiver", pgconn_m_PQsetNoticeReceiver, 2, 0, 0 },
+       { "setNoticeProcessor", pgconn_m_PQsetNoticeProcessor, 2, 0, 0 },
        { NULL, NULL, 0, 0, 0 }
 };
 
@@ -778,6 +783,11 @@ pg_sm_PQconnectdb(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
                QUEUE_EXCEPTION("Internal error!");
                goto err;
        }
+       /* Add reverse resolve entry */
+       if (!js_map_add(pgc, cx, o, NULL)) {
+               QUEUE_EXCEPTION("js_map_add()");
+               goto err;
+       }
 
        return JS_TRUE;
 
@@ -823,6 +833,11 @@ pg_sm_PQconnectStart(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
                QUEUE_EXCEPTION("Internal error!");
                goto err;
        }
+       /* Reverse resolve entry */
+       if (!js_map_add(pgc, cx, o, NULL)) {
+               QUEUE_EXCEPTION("js_map_add()");
+               goto err;
+       }
 
        return JS_TRUE;
 
@@ -945,8 +960,12 @@ pgconn_finalize(JSContext *cx, JSObject *obj)
 
        if ((pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL))
            != NULL) {
+               omap_t  *omap;
+
                PQfinish(pgc);
                (void) JS_SetPrivate(cx, obj, NULL);
+               if ((omap = js_map_lookup(pgc)) != NULL)
+                       js_map_remove(omap);
        }
 }
 
@@ -969,8 +988,12 @@ pgconn_m_PQfinish(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
        if ((pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL))
            != NULL) {
+               omap_t  *omap;
+
                PQfinish(pgc);
                (void) JS_SetPrivate(cx, obj, NULL);
+               if ((omap = js_map_lookup(pgc)))
+                       js_map_remove(omap);
        }
 
        *rval = OBJECT_TO_JSVAL(NULL);
@@ -1430,6 +1453,11 @@ pgconn_m_PQexec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
                QUEUE_EXCEPTION("Internal error!");
                goto err;
        }
+       /* Record reverse resolve entry */
+       if (!js_map_add(pgr, cx, o, obj)) {
+               QUEUE_EXCEPTION("js_map_add()");
+               goto err;
+       }
 
        return JS_TRUE;
 
@@ -1705,6 +1733,11 @@ pgconn_m_PQexecParams2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
                        QUEUE_EXCEPTION("Internal error!");
                        goto err;
                }
+               /* Reverse resolve entry */
+               if (!js_map_add(pgr, cx, o, obj)) {
+                       QUEUE_EXCEPTION("js_map_add()");
+                       goto err;
+               }
 
        } else {
 
@@ -1870,6 +1903,11 @@ pgconn_m_PQprepare2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
                        QUEUE_EXCEPTION("Internal error!");
                        goto err;
                }
+               /* Reverse resolve entry */
+               if (!js_map_add(pgr, cx, o, obj)) {
+                       QUEUE_EXCEPTION("js_map_add()");
+                       goto err;
+               }
 
        } else {
 
@@ -2106,6 +2144,11 @@ pgconn_m_PQexecPrepared2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
                        QUEUE_EXCEPTION("Internal error!");
                        goto err;
                }
+               /* Reverse resolve entry */
+               if (!js_map_add(pgr, cx, o, obj)) {
+                       QUEUE_EXCEPTION("js_map_add()");
+                       goto err;
+               }
 
        } else {
 
@@ -2184,6 +2227,11 @@ pgconn_m_PQmakeEmptyPGresult(JSContext *cx, JSObject *obj, uintN argc,
                QUEUE_EXCEPTION("Internal error!");
                goto err;
        }
+       /* Revere resolve entry */
+       if (!js_map_add(pgr, cx, o, obj)) {
+               QUEUE_EXCEPTION("js_map_add()");
+               goto err;
+       }
 
        return JS_TRUE;
 
@@ -2450,6 +2498,11 @@ pgconn_m_PQgetResult(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
                QUEUE_EXCEPTION("Internal error!");
                goto err;
        }
+       /* Reverse resolve entry */
+       if (!js_map_add(pgr, cx, o, obj)) {
+               QUEUE_EXCEPTION("js_map_add()");
+               goto err;
+       }
 
        return JS_TRUE;
 
@@ -2803,6 +2856,232 @@ err:
        return JS_FALSE;
 }
 
+static JSBool
+pgconn_m_PQsetNoticeReceiver(JSContext *cx, JSObject *obj, uintN argc,
+    jsval *argv, jsval *rval)
+{
+       PGconn  *pgc;
+
+       /*
+        * Verify if our connection object has the np_function property set,
+        * in which case we set it as the return code.  If it doesn't have
+        * any, return null.
+        */
+       if (!JS_GetProperty(cx, obj, "np_function", &rval[0]))
+               *rval = OBJECT_TO_JSVAL(NULL);
+
+       if (argc != 2) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_OBJECT(argv[0]) ||
+           !JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(argv[0]))) {
+               QUEUE_EXCEPTION("Argument 1 not a Function Object");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_OBJECT(argv[1]) && !JSVAL_IS_NULL(argv[1])) {
+               QUEUE_EXCEPTION("Argument 2 not an Object or null");
+               return JS_FALSE;
+       }
+
+       /*
+        * Set the np_function and np_udata properties.  This simplifies
+        * things while ensuring that the ojects live as long as the
+        * connection object does.  We don't make them enumerable.
+        */
+       if (!JS_DefineProperty(cx, obj, "np_function", argv[0], NULL, NULL,
+           JSPROP_READONLY | JSPROP_PERMANENT)) {
+               QUEUE_EXCEPTION("Internal error!");
+               return JS_FALSE;
+       }
+       if (!JS_DefineProperty(cx, obj, "np_udata", argv[1], NULL, NULL,
+           JSPROP_READONLY | JSPROP_PERMANENT)) {
+               QUEUE_EXCEPTION("Internal error!");
+               return JS_FALSE;
+       }
+
+       /*
+        * Now set our custom notice_processor function.  We need to pass both
+        * cx and obj to it, although we can only pass a single pointer.  And
+        * we can't set obj as cx specific data, since the data must be
+        * connection specific.  Moreover, we can't store private data on the
+        * supplied function object, since the JSFunction * is set already.
+        * Moreover, JS_GetPrivate()/JS_GetInstancePrivate() both need the
+        * JSObject * AND the JSContext *!  This means that I need
+        * connection-specific udata other than PGconn *, since it would not
+        * be possible to trace back the allocated data at object
+        * finalization if we just allocated memory and provided the pointer
+        * as udata.  Unless I stored the pointer as a property in the
+        * connection object, heh.
+        */
+       pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
+       assert(pgc != NULL);
+
+       (void) PQsetNoticeReceiver(pgc, notice_receiver, NULL);
+
+       return JS_TRUE;
+}
+
+static void
+notice_receiver(void *udata, const PGresult *pgr)
+{
+       omap_t  *omap;
+       jsval   args[2], ret, pgrv, np_function, np_udata;
+
+       omap = js_map_lookup((void *)pgr);
+       assert(omap != NULL);
+
+       /*
+        * JSContext       *cx;
+        * JSObject        *obj;                PGResult
+        * void            *udata;              PGConn JSObject *
+        */
+
+       pgrv = OBJECT_TO_JSVAL(omap->obj);
+
+       /* We call the user supplied function with the arguments:
+        * udata, PGResult object.
+        * We root the PGResult object to the PGConn meanwhile.
+        * We get out the udata and function to call from the connection
+        * object.
+        */
+       if (!JS_DefineProperty(omap->cx, omap->udata, "np_pgresult", pgrv,
+           NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT))
+               return;
+
+       if (!JS_GetProperty(omap->cx, omap->udata, "np_function",
+           &np_function) || !JS_GetProperty(omap->cx, omap->udata,
+           "np_udata", &np_udata))
+               goto err;
+
+       args[0] = np_udata;
+       args[1] = pgrv;
+
+       (void) JS_CallFunctionValue(omap->cx, omap->udata, np_function, 2,
+           args, &ret);
+
+err:
+       (void) JS_DeleteProperty(omap->cx, omap->udata, "np_pgresult");
+}
+
+static JSBool
+pgconn_m_PQsetNoticeProcessor(JSContext *cx, JSObject *obj, uintN argc,
+    jsval *argv, jsval *rval)
+{
+       PGconn  *pgc;
+
+       /*
+        * Verify if our connection object has the np_function property set,
+        * in which case we set it as the return code.  If it doesn't have
+        * any, return null.
+        */
+       if (!JS_GetProperty(cx, obj, "np_function", &rval[0]))
+               *rval = OBJECT_TO_JSVAL(NULL);
+
+       if (argc != 2) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_OBJECT(argv[0]) ||
+           !JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(argv[0]))) {
+               QUEUE_EXCEPTION("Argument 1 not a Function Object");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_OBJECT(argv[1]) && !JSVAL_IS_NULL(argv[1])) {
+               QUEUE_EXCEPTION("Argument 2 not an Object or null");
+               return JS_FALSE;
+       }
+
+       /*
+        * Set the np_function and np_udata properties.  This simplifies
+        * things while ensuring that the ojects live as long as the
+        * connection object does.  We don't make them enumerable.
+        */
+       if (!JS_DefineProperty(cx, obj, "np_function", argv[0], NULL, NULL,
+           JSPROP_READONLY | JSPROP_PERMANENT)) {
+               QUEUE_EXCEPTION("Internal error!");
+               return JS_FALSE;
+       }
+       if (!JS_DefineProperty(cx, obj, "np_udata", argv[1], NULL, NULL,
+           JSPROP_READONLY | JSPROP_PERMANENT)) {
+               QUEUE_EXCEPTION("Internal error!");
+               return JS_FALSE;
+       }
+
+       /*
+        * Now set our custom notice_processor function.  We need to pass both
+        * cx and obj to it, although we can only pass a single pointer.  And
+        * we can't set obj as cx specific data, since the data must be
+        * connection specific.  Moreover, we can't store private data on the
+        * supplied function object, since the JSFunction * is set already.
+        * Moreover, JS_GetPrivate()/JS_GetInstancePrivate() both need the
+        * JSObject * AND the JSContext *!  This means that I need
+        * connection-specific udata other than PGconn *, since it would not
+        * be possible to trace back the allocated data at object
+        * finalization if we just allocated memory and provided the pointer
+        * as udata.  Unless I stored the pointer as a property in the
+        * connection object, heh.
+        */
+       pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
+       assert(pgc != NULL);
+
+       (void) PQsetNoticeProcessor(pgc, notice_processor, pgc);
+
+       return JS_TRUE;
+}
+
+/* Internal C notive processor handler which delegates to JS */
+static void
+notice_processor(void *udata, const char *msg)
+{
+       omap_t          *omap;
+       PGconn          *pgc;
+       jsval           args[2], ret, strv, np_function, np_udata;
+       JSString        *str;
+
+       /*
+        * The pgc pointer is passed through udata.  Lookup by pgc to obtain
+        * the corresponding PGConn JSObject * and its JSContext *.
+        */
+       pgc = udata;
+       omap = js_map_lookup(pgc);
+       assert(omap != NULL);
+
+       /*
+        * JSContext    *cx;
+        * JSObject     *obj;           PGConn
+        * void         *udata;
+        */
+
+       if ((str = JS_NewStringCopyZ(omap->cx, msg)) == NULL)
+               return;
+       strv = STRING_TO_JSVAL(str);
+
+       /*
+        * Obtain np_function and np_udata from the connection object we were
+        * supplied with, and all the function with parameters:
+        * udata, String.
+        * Temporarily root the JSString meanwhile.
+        */
+       if (!JS_DefineProperty(omap->cx, omap->obj, "np_message", strv,
+           NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT))
+               return;
+
+       if (!JS_GetProperty(omap->cx, omap->udata, "np_function",
+           &np_function) || !JS_GetProperty(omap->cx, omap->udata,
+           "np_udata", &np_udata))
+               goto err;
+
+       args[0] = np_udata;
+       args[1] = strv;
+
+       (void) JS_CallFunctionValue(omap->cx, omap->obj, np_function, 2,
+           args, &ret);
+
+err:
+       (void) JS_DeleteProperty(omap->cx, omap->obj, "np_message");
+}
+
 
 /*
  * PGresult object control
@@ -2834,8 +3113,12 @@ pgresult_finalize(JSContext *cx, JSObject *obj)
 
        if ((pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL))
            != NULL) {
+               omap_t  *omap;
+
                PQclear(pgr);
                (void) JS_SetPrivate(cx, obj, NULL);
+               if ((omap = js_map_lookup(pgr)) != NULL)
+                       js_map_remove(omap);
        }
 }
 
@@ -2858,8 +3141,12 @@ pgresult_m_PQclear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
        if ((pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL))
            != NULL) {
+               omap_t  *omap;
+
                PQclear(pgr);
                (void) JS_SetPrivate(cx, obj, NULL);
+               if ((omap = js_map_lookup(pgr)) != NULL)
+                       js_map_remove(omap);
        }
 
        *rval = OBJECT_TO_JSVAL(NULL);
index 3a3c2b8..a75abe1 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: js-appserv.c,v 1.3 2006/10/05 18:43:36 mmondor Exp $ */
+/* $Id: js-appserv.c,v 1.4 2006/10/18 05:07:45 mmondor Exp $ */
 
 /*
  * Copyright (C) 2006, Matthew Mondor
@@ -220,6 +220,7 @@ static JSClass class_global = {
  * children processes.
  */
 JSRuntime      *p_rt = NULL;
+JSBool         p_map = JS_FALSE;
 JSContext      *p_ctx = NULL;
 JSObject       *p_obj = NULL;
 JSScript       *p_script = NULL;
@@ -714,6 +715,7 @@ static int
 script_reload(const char *file)
 {
        JSRuntime       *rt = NULL;
+       JSBool          map = JS_FALSE;
        JSContext       *ctx = NULL;
        JSObject        *obj, *robj;
        JSScript        *script = NULL;
@@ -730,6 +732,10 @@ script_reload(const char *file)
                        JS_DestroyContext(p_ctx);
                        p_ctx = NULL;
                }
+               if (p_map) {
+                       js_map_destroy();
+                       p_map = JS_FALSE;
+               }
                if (p_rt != NULL) {
                        JS_DestroyRuntime(p_rt);
                        p_rt = NULL;
@@ -742,6 +748,10 @@ script_reload(const char *file)
                syslog(LOG_NOTICE, "script_reload() - JS_NewRuntime()");
                goto err;
        }
+       if (!(map = js_map_init())) {
+               syslog(LOG_NOTICE, "script_reload() - js_map_init()");
+               goto err;
+       }
        if ((ctx = context_create(rt, CONF.JS_STACK_SIZE * 1024, &obj))
            == NULL) {
                syslog(LOG_NOTICE, "script_reload() - context_create()");
@@ -774,6 +784,7 @@ script_reload(const char *file)
        p_obj = obj;
        p_script = script;
        p_ctx = ctx;
+       p_map = map;
        p_rt = rt;
 
        return 0;
@@ -785,6 +796,8 @@ err:
                js_DestroyGCRoot(ctx);
                JS_DestroyContext(ctx);
        }
+       if (map)
+               js_map_destroy();
        if (rt)
                JS_DestroyRuntime(rt);
 
index 40bab4e..c74408c 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: GNUmakefile,v 1.5 2006/10/05 18:43:38 mmondor Exp $
+# $Id: GNUmakefile,v 1.6 2006/10/18 05:07:48 mmondor Exp $
 
 #CFLAGS += -g
 CFLAGS += -Wall
@@ -14,21 +14,24 @@ GD_CFLAGS := $(shell gdlib-config --cflags)
 GD_LDFLAGS := $(shell gdlib-config --ldflags --libs)
 GD_LDFLAGS += -lgd
 
-OBJS := $(addprefix ../../classes/,js_gcroot.o js_fd.o js_errno.o js_signal.o \
-       js_file.o js_pgsql.o js_dir.o js_fs.o js_gd.o)
-OBJS += js-sh.o
+MMOBJS := $(addprefix ../../../mmlib/,mmpool.o mmhash.o mmlog.o mmstring.o)
+JSOBJS := $(addprefix ../../classes/,js_gcroot.o js_fd.o js_errno.o \
+       js_signal.o js_file.o js_pgsql.o js_dir.o js_fs.o js_gd.o)
 
-CFLAGS += $(JS_CFLAGS) $(PG_CFLAGS) $(GD_CFLAGS) -I../../classes -Wall
+CFLAGS += $(JS_CFLAGS) $(PG_CFLAGS) $(GD_CFLAGS) -I../../../mmlib \
+       -I../../classes
 LDFLAGS += $(JS_LDFLAGS) $(PG_LDFLAGS) $(GD_LDFLAGS)
 
+OBJ := js-sh.o
+
 
 all: js-sh
 
 %.o: %.c
        cc -c $(CFLAGS) -o $@ $<
 
-js-sh: $(OBJS)
-       cc -o $@ -lc $(LDFLAGS) $(OBJS)
+js-sh: $(OBJ) $(MMOBJS) $(JSOBJS)
+       cc -o $@ $(OBJ) $(LDFLAGS) -lc $(MMOBJS) $(JSOBJS)
 
 clean:
-       rm -f js-sh $(OBJS)
+       rm -f js-sh $(OBJ) $(MMOBJS) $(JSOBJS)
index 09be511..a2a49d2 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: js-sh.c,v 1.7 2006/10/05 18:43:38 mmondor Exp $ */
+/* $Id: js-sh.c,v 1.8 2006/10/18 05:07:48 mmondor Exp $ */
 
 /*
  * Copyright (c) 2004-2005, Matthew Mondor
@@ -230,6 +230,7 @@ js_context_init(size_t gc_size, size_t stack_size)
 
        if ((cctx = malloc(sizeof(js_context_t))) == NULL ||
            (cctx->rt = JS_NewRuntime(gc_size)) == NULL ||
+           !js_map_init() ||
            (cctx->ctx = JS_NewContext(cctx->rt, stack_size)) == NULL ||
            (cctx->global = JS_NewObject(cctx->ctx, &global_class, NULL,
                NULL)) == NULL ||
@@ -269,6 +270,7 @@ js_context_destroy(js_context_t *cctx)
                js_DestroyGCRoot(cctx->ctx);
                JS_DestroyContext(cctx->ctx);
        }
+       js_map_destroy();
        if (cctx->rt != NULL)
                JS_DestroyRuntime(cctx->rt);