changeset 145:5d53f6293a81

Added a basic context.get_stack() function.
author Atul Varma <varmaa@toolness.com>
date Fri, 28 Aug 2009 22:46:07 -0700
parents ab612d2ad96a
children b1cf9decc36f
files src/context.cpp tests/test_pymonkey.py
diffstat 2 files changed, 101 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/src/context.cpp	Mon Aug 24 22:47:15 2009 -0700
+++ b/src/context.cpp	Fri Aug 28 22:46:07 2009 -0700
@@ -40,6 +40,8 @@
 #include "script.h"
 #include "utils.h"
 
+#include "jsdbgapi.h"
+
 // This is the default JSOperationCallback for pymonkey-owned JS contexts,
 // when they've defined one in Python.
 static JSBool
@@ -125,6 +127,60 @@
 }
 
 static PyObject *
+PYM_getStack(PYM_JSContextObject *self, PyObject *args)
+{
+  PYM_SANITY_CHECK(self->runtime);
+
+  JSStackFrame *iteratorp = NULL;
+  JSStackFrame *frame;
+  PyObject *top = NULL;
+  // This is always a borrowed reference, i.e. we never incref/decref it.
+  PyObject *last = NULL;
+
+  while ((frame = JS_FrameIterator(self->cx, &iteratorp)) != NULL) {
+    bool success = true;
+    JSScript *script = JS_GetFrameScript(self->cx, frame);
+    PyObject *pyScript;
+    if (script)
+      pyScript = (PyObject *) PYM_newJSScript(self, script);
+    else {
+      pyScript = Py_None;
+      Py_INCREF(pyScript);
+    }
+
+    PyObject *frameDict = Py_BuildValue(
+      "{sO}",
+      "script", pyScript
+      );
+
+    Py_XDECREF(pyScript);
+
+    if (frameDict) {
+      if (last) {
+        if (PyDict_SetItemString(last, "caller", frameDict) == 0) {
+          last = frameDict;
+          Py_DECREF(frameDict);
+          frameDict = NULL;
+        } else
+          success = false;
+      } else {
+        top = frameDict;
+        last = frameDict;
+      }
+    } else
+      success = false;
+
+    if (!success) {
+      Py_XDECREF(top);
+      Py_XDECREF(frameDict);
+      return NULL;
+    }
+  }
+
+  return top;
+}
+
+static PyObject *
 PYM_getObjectPrivate(PYM_JSContextObject *self, PyObject *args)
 {
   PYM_SANITY_CHECK(self->runtime);
@@ -335,6 +391,8 @@
     return NULL;
   }
 
+  // TODO: If this somehow fails, we may have a memory leak if a
+  // script object wasn't created for the JSScript.
   return (PyObject *) PYM_newJSScript(self, script);
 }
 
@@ -385,13 +443,37 @@
 
   PYM_ENSURE_RUNTIME_MATCH(self->runtime, object->runtime);
 
+  // Instead of calling JS_EvaluateUCScript(), we're going to first
+  // compile the script and then execute it. This is because the
+  // former function calls JS_DestroyScript() on the script it's
+  // created, which prevents e.g. the script object from being
+  // extracted during execution and outliving the script's execution.
+
+  JSScript *script;
+  script = JS_CompileUCScript(self->cx, NULL, str.jsbuffer,
+                              str.jslen, filename, lineNo);
+
+  if (script == NULL) {
+    PYM_jsExceptionToPython(self);
+    return NULL;
+  }
+
+  // TODO: If this somehow fails, we may have a memory leak if a
+  // script object wasn't created for the JSScript.
+  PYM_JSScript *pyScript = PYM_newJSScript(self, script);
+
+  if (pyScript == NULL) {
+    PYM_jsExceptionToPython(self);
+    return NULL;
+  }
+
   jsval rval;
   JSBool result;
   Py_BEGIN_ALLOW_THREADS;
-  result = JS_EvaluateUCScript(self->cx, object->obj, str.jsbuffer,
-                               str.jslen, filename, lineNo, &rval);
+  result = JS_ExecuteScript(self->cx, object->obj, pyScript->script, &rval);
   Py_END_ALLOW_THREADS;
 
+  Py_DECREF((PyObject *) pyScript);
   if (!result) {
     PYM_jsExceptionToPython(self);
     return NULL;
@@ -537,6 +619,8 @@
 static PyMethodDef PYM_JSContextMethods[] = {
   {"get_runtime", (PyCFunction) PYM_getRuntime, METH_VARARGS,
    "Get the JavaScript runtime associated with this context."},
+  {"get_stack", (PyCFunction) PYM_getStack, METH_VARARGS,
+   "Get the current stack for the context."},
   {"new_object", (PyCFunction) PYM_newObject, METH_VARARGS,
    "Create a new JavaScript object."},
   {"init_standard_classes",
--- a/tests/test_pymonkey.py	Mon Aug 24 22:47:15 2009 -0700
+++ b/tests/test_pymonkey.py	Fri Aug 28 22:46:07 2009 -0700
@@ -48,6 +48,21 @@
                 u'SyntaxError: missing ; before statement'
                 )
 
+    def testGetStackWorks(self):
+        stack_holder = []
+
+        def func(cx, this, args):
+            stack_holder.append(cx.get_stack())
+
+        cx = pymonkey.Runtime().new_context()
+        obj = cx.new_object()
+        cx.init_standard_classes(obj)
+        jsfunc = cx.new_function(func, func.__name__)
+        cx.define_property(obj, func.__name__, jsfunc)
+        cx.evaluate_script(obj, 'func()', '<string>', 1)
+        self.assertEqual(stack_holder[0]['caller']['script'].filename,
+                         '<string>')
+
     def testScriptHasFilenameMember(self):
         cx = pymonkey.Runtime().new_context()
         script = cx.compile_script('foo', '<string>', 1)