# HG changeset patch # User Atul Varma # Date 1248637411 25200 # Node ID fb7e11dec538e01e74bdc3904236d145d962aeb9 # Parent f3ecf06a28513f2e6de153cdc1618cd4c011df3b Added context.set_operation_callback() and trigger_operation_callback() methods. diff -r f3ecf06a2851 -r fb7e11dec538 context.c --- a/context.c Sat Jul 25 23:08:47 2009 -0700 +++ b/context.c Sun Jul 26 12:43:31 2009 -0700 @@ -39,6 +39,29 @@ #include "function.h" #include "utils.h" +static JSBool +PYM_operationCallback(JSContext *cx) +{ + PYM_JSContextObject *context = (PYM_JSContextObject *) + JS_GetContextPrivate(cx); + + PyObject *callable = context->opCallback; + PyObject *args = PyTuple_Pack(1, (PyObject *) context); + if (args == NULL) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + PyObject *result = PyObject_Call(callable, args, NULL); + Py_DECREF(args); + if (result == NULL) { + PYM_pythonExceptionToJs(context); + return JS_FALSE; + } + + Py_DECREF(result); + return JS_TRUE; +} + // This is the default JSErrorReporter for pymonkey-owned JS contexts. static void PYM_reportError(JSContext *cx, const char *message, JSErrorReport *report) @@ -59,6 +82,11 @@ static void PYM_JSContextDealloc(PYM_JSContextObject *self) { + if (self->opCallback) { + Py_DECREF(self->opCallback); + self->opCallback = NULL; + } + if (self->cx) { JS_DestroyContext(self->cx); self->cx = NULL; @@ -259,6 +287,36 @@ return (PyObject *) PYM_newJSFunctionFromCallable(self, callable, name); } +static PyObject * +PYM_setOperationCallback(PYM_JSContextObject *self, PyObject *args) +{ + PyObject *callable; + + if (!PyArg_ParseTuple(args, "O", &callable)) + return NULL; + + if (!PyCallable_Check(callable)) { + PyErr_SetString(PyExc_TypeError, "Callable must be callable"); + return NULL; + } + + JS_SetOperationCallback(self->cx, PYM_operationCallback); + + Py_INCREF(callable); + if (self->opCallback) + Py_DECREF(self->opCallback); + self->opCallback = callable; + + Py_RETURN_NONE; +} + +static PyObject * +PYM_triggerOperationCallback(PYM_JSContextObject *self, PyObject *args) +{ + JS_TriggerOperationCallback(self->cx); + Py_RETURN_NONE; +} + static PyMethodDef PYM_JSContextMethods[] = { {"get_runtime", (PyCFunction) PYM_getRuntime, METH_VARARGS, "Get the JavaScript runtime associated with this context."}, @@ -285,6 +343,12 @@ "Gets the given property for the given JavaScript object."}, {"gc", (PyCFunction) PYM_gc, METH_VARARGS, "Performs garbage collection on the context's runtime."}, + {"set_operation_callback", (PyCFunction) PYM_setOperationCallback, + METH_VARARGS, + "Sets the operation callback for the context."}, + {"trigger_operation_callback", (PyCFunction) PYM_triggerOperationCallback, + METH_VARARGS, + "Triggers the operation callback for the context."}, {NULL, NULL, 0, NULL} }; @@ -340,6 +404,7 @@ if (context == NULL) return NULL; + context->opCallback = NULL; context->runtime = runtime; Py_INCREF(runtime); diff -r f3ecf06a2851 -r fb7e11dec538 context.h --- a/context.h Sat Jul 25 23:08:47 2009 -0700 +++ b/context.h Sun Jul 26 12:43:31 2009 -0700 @@ -46,6 +46,7 @@ PyObject_HEAD PYM_JSRuntimeObject *runtime; JSContext *cx; + PyObject *opCallback; } PYM_JSContextObject; extern PyTypeObject PYM_JSContextType; diff -r f3ecf06a2851 -r fb7e11dec538 test_pymonkey.py --- a/test_pymonkey.py Sat Jul 25 23:08:47 2009 -0700 +++ b/test_pymonkey.py Sun Jul 26 12:43:31 2009 -0700 @@ -29,6 +29,30 @@ was_raised = True self.assertTrue(was_raised) + def testOperationCallbackIsCalled(self): + def opcb(cx): + raise Exception("stop eet!") + + cx = pymonkey.Runtime().new_context() + cx.set_operation_callback(opcb) + obj = cx.new_object() + cx.init_standard_classes(obj) + + # TODO: This isn't a very good test; we need to actually + # set up a signal or launch a separate thread to call + # this method as though it were a watchdog to limit the + # amount of time the JS can run. However, Pymonkey doesn't + # yet handle the GIL properly so this isn't possible. + cx.trigger_operation_callback() + + self.assertRaises( + pymonkey.error, + cx.evaluate_script, + obj, 'while (1) {}', '', 1 + ) + self.assertEqual(self.last_exception.message, + "stop eet!") + def testUndefinedStrIsUndefined(self): self.assertEqual(str(pymonkey.undefined), "pymonkey.undefined")