changeset 154:5ec81091cb89

Added filename/line-number metadata to functions. Wanted to just add a 'script' property but this proved problematic--see the TODO in the code for more information.
author Atul Varma <varmaa@toolness.com>
date Sun, 30 Aug 2009 11:13:00 -0700
parents 9ea6a9a566e2
children b0c9d6884da3
files src/function.cpp src/function.h tests/test_pymonkey.py
diffstat 3 files changed, 41 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/src/function.cpp	Sun Aug 30 10:54:56 2009 -0700
+++ b/src/function.cpp	Sun Aug 30 11:13:00 2009 -0700
@@ -37,12 +37,14 @@
 #include "function.h"
 #include "utils.h"
 
+#include "jsdbgapi.h"
 #include "structmember.h"
 
 static void
 PYM_JSFunctionDealloc(PYM_JSFunction *self)
 {
   self->fun = NULL;
+  self->filename = NULL;
 
   if (self->name) {
     Py_DECREF(self->name);
@@ -136,6 +138,12 @@
 static PyMemberDef PYM_members[] = {
   {"name", T_OBJECT, offsetof(PYM_JSFunction, name), READONLY,
    "Name of the function."},
+  {"filename", T_STRING, offsetof(PYM_JSFunction, filename), READONLY,
+   "Filename of function's source code."},
+  {"base_lineno", T_UINT, offsetof(PYM_JSFunction, baseLineno), READONLY,
+   "Base line number of function's source code."},
+  {"line_extent", T_UINT, offsetof(PYM_JSFunction, lineExtent), READONLY,
+   "Line extent of function's source code."},
   {NULL, NULL, NULL, NULL, NULL}
 };
 
@@ -194,12 +202,14 @@
     return NULL;
 
   jsFunction->fun = function;
+  jsFunction->name = NULL;
+  jsFunction->filename = NULL;
+  jsFunction->baseLineno = 0;
+  jsFunction->lineExtent = 0;
 
   JSString *name = JS_GetFunctionId(jsFunction->fun);
-  if (name == NULL) {
-    // It's an anonymous function.
-    jsFunction->name = NULL;
-  } else {
+  if (name != NULL) {
+    // It's not an anonymous function.
     jsFunction->name = PYM_jsvalToPyObject(context,
                                            STRING_TO_JSVAL(name));
     if (jsFunction->name == NULL) {
@@ -208,6 +218,19 @@
     }
   }
 
+  JSScript *script = JS_GetFunctionScript(context->cx, jsFunction->fun);
+
+  // TODO: Ideally, we'd convert the script to an object and set it as
+  // an attribute of the function, but this results in strange segfaults,
+  // perhaps because JS functions destroy their scripts on finalization
+  // while creating an object from a script makes it subject to GC.
+  if (script) {
+    // It's an interpreted function.
+    jsFunction->filename = JS_GetScriptFilename(context->cx, script);
+    jsFunction->baseLineno = JS_GetScriptBaseLineNumber(context->cx, script);
+    jsFunction->lineExtent = JS_GetScriptLineExtent(context->cx, script);
+  }
+
   return jsFunction;
 }
 
--- a/src/function.h	Sun Aug 30 10:54:56 2009 -0700
+++ b/src/function.h	Sun Aug 30 11:13:00 2009 -0700
@@ -47,6 +47,9 @@
   PYM_JSObject base;
   JSFunction *fun;
   PyObject *name;
+  const char *filename;
+  unsigned int baseLineno;
+  unsigned int lineExtent;
 } PYM_JSFunction;
 
 extern PyTypeObject PYM_JSFunctionType;
--- a/tests/test_pymonkey.py	Sun Aug 30 10:54:56 2009 -0700
+++ b/tests/test_pymonkey.py	Sun Aug 30 11:13:00 2009 -0700
@@ -660,6 +660,17 @@
         self.assertTrue(isinstance(obj, pymonkey.Object))
         self.assertEqual(cx.get_property(obj, u"boop"), 1)
 
+    def testScriptedFunctionsHaveFilenameInfo(self):
+        cx = pymonkey.Runtime().new_context()
+        obj = cx.new_object()
+        cx.init_standard_classes(obj)
+        jsfunc = cx.evaluate_script(obj,
+                                    '(function boop() { \nreturn 1; })',
+                                    'somefile', 5)
+        self.assertEqual(jsfunc.filename, 'somefile')
+        self.assertEqual(jsfunc.base_lineno, 5)
+        self.assertEqual(jsfunc.line_extent, 2)
+
     def testEvaluateReturnsFunction(self):
         cx = pymonkey.Runtime().new_context()
         obj = cx.new_object()