changeset 165:1f9a5982db9c

Added a context.set_throw_hook() method.
author Atul Varma <varmaa@toolness.com>
date Sun, 30 Aug 2009 19:03:46 -0700
parents 3fadba042201
children 2eaae00d5e30
files src/context.cpp src/context.h tests/test_pymonkey.py
diffstat 3 files changed, 78 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/context.cpp	Sun Aug 30 17:07:38 2009 -0700
+++ b/src/context.cpp	Sun Aug 30 19:03:46 2009 -0700
@@ -43,6 +43,33 @@
 #include "jsdbgapi.h"
 #include "jsscript.h"
 
+// This is the default throw hook for pymonkey-owned JS contexts,
+// when they've defined one in Python.
+static JSTrapStatus
+PYM_throwHook(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
+              void *closure)
+{
+  PYM_PyAutoEnsureGIL gil;
+  PYM_JSContextObject *context = (PYM_JSContextObject *)
+    JS_GetContextPrivate(cx);
+
+  PyObject *callable = context->throwHook;
+  PyObject *args = PyTuple_Pack(1, (PyObject *) context);
+  if (args == NULL) {
+    JS_ReportOutOfMemory(cx);
+    return JSTRAP_CONTINUE;
+  }
+  PyObject *result = PyObject_Call(callable, args, NULL);
+  Py_DECREF(args);
+  if (result == NULL) {
+    PYM_pythonExceptionToJs(context);
+    return JSTRAP_CONTINUE;
+  }
+
+  Py_DECREF(result);
+  return JSTRAP_CONTINUE;
+}
+
 // This is the default JSOperationCallback for pymonkey-owned JS contexts,
 // when they've defined one in Python.
 static JSBool
@@ -92,6 +119,7 @@
 PYM_traverse(PYM_JSContextObject *self, visitproc visit, void *arg)
 {
   Py_VISIT(self->opCallback);
+  Py_VISIT(self->throwHook);
   Py_VISIT(self->runtime);
   return 0;
 }
@@ -100,6 +128,7 @@
 PYM_clear(PYM_JSContextObject *self)
 {
   Py_CLEAR(self->opCallback);
+  Py_CLEAR(self->throwHook);
   Py_CLEAR(self->runtime);
   return 0;
 }
@@ -630,6 +659,31 @@
 }
 
 static PyObject *
+PYM_setThrowHook(PYM_JSContextObject *self, PyObject *args)
+{
+  PYM_SANITY_CHECK(self->runtime);
+  PyObject *callable;
+
+  if (!PyArg_ParseTuple(args, "O", &callable))
+    return NULL;
+
+  if (!PyCallable_Check(callable)) {
+    PyErr_SetString(PyExc_TypeError, "Callable must be callable");
+    return NULL;
+  }
+
+  self->hooks.throwHook = PYM_throwHook;
+  JS_SetContextDebugHooks(self->cx, &self->hooks);
+
+  Py_INCREF(callable);
+  if (self->throwHook)
+    Py_DECREF(self->throwHook);
+  self->throwHook = callable;
+
+  Py_RETURN_NONE;
+}
+
+static PyObject *
 PYM_setOperationCallback(PYM_JSContextObject *self, PyObject *args)
 {
   PYM_SANITY_CHECK(self->runtime);
@@ -701,6 +755,8 @@
   {"set_operation_callback", (PyCFunction) PYM_setOperationCallback,
    METH_VARARGS,
    "Sets the operation callback for the context."},
+  {"set_throw_hook", (PyCFunction) PYM_setThrowHook, METH_VARARGS,
+   "Sets the throw hook for the context."},
   {"trigger_operation_callback", (PyCFunction) PYM_triggerOperationCallback,
    METH_VARARGS,
    "Triggers the operation callback for the context."},
@@ -765,8 +821,11 @@
   if (context == NULL)
     return NULL;
 
+  memset(&context->hooks, 0, sizeof(context->hooks));
+
   context->weakrefs = NULL;
   context->opCallback = NULL;
+  context->throwHook = NULL;
   context->runtime = runtime;
   Py_INCREF(runtime);
 
--- a/src/context.h	Sun Aug 30 17:07:38 2009 -0700
+++ b/src/context.h	Sun Aug 30 19:03:46 2009 -0700
@@ -40,6 +40,7 @@
 #include "runtime.h"
 
 #include <jsapi.h>
+#include <jsdbgapi.h>
 #include <Python.h>
 
 typedef struct {
@@ -47,7 +48,9 @@
   PYM_JSRuntimeObject *runtime;
   JSContext *cx;
   PyObject *opCallback;
+  PyObject *throwHook;
   PyObject *weakrefs;
+  JSDebugHooks hooks;
 } PYM_JSContextObject;
 
 extern PyTypeObject PYM_JSContextType;
--- a/tests/test_pymonkey.py	Sun Aug 30 17:07:38 2009 -0700
+++ b/tests/test_pymonkey.py	Sun Aug 30 19:03:46 2009 -0700
@@ -232,6 +232,22 @@
                                     '(function(){})', '<string>', 1)
         self.assertEqual(cx.get_object_private(jsfunc), None)
 
+    def testThrowHookWorks(self):
+        timesCalled = [0]
+        def throwhook(cx):
+            timesCalled[0] += 1
+
+        cx = pymonkey.Runtime().new_context()
+        cx.set_throw_hook(throwhook)
+        self.assertRaises(
+            pymonkey.error,
+            cx.evaluate_script,
+            cx.new_object(),
+            '(function() { throw "hi"; })()',
+            '<string>', 1
+            )
+        self.assertEqual(timesCalled[0], 2)
+
     def testJsWrappedPythonFuncHasPrivate(self):
         def foo(cx, this, args):
             pass