[gjs] Stop walking the stack manually



commit 6cdf959de489502e11a713b8a5e977618325f677
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Tue May 7 13:28:39 2013 +0200

    Stop walking the stack manually
    
    In JS 17, and when dealing with optimized frames without "arguments",
    our technique to build the stack can fail and throw an exception.
    Normally we would override it when we set the real exception, but
    in the GDBus test suite we didn't, so there was always this
    unnoticed "Debugger scope is not live" error, until it was made
    fatal by glib changes.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=690984

 gi/gerror.c      |   44 ++---------
 gjs/context.h    |    7 --
 gjs/jsapi-util.h |    5 +
 gjs/stack.c      |  219 ++++++++++++------------------------------------------
 4 files changed, 61 insertions(+), 214 deletions(-)
---
diff --git a/gi/gerror.c b/gi/gerror.c
index 3bcad05..77a1a7c 100644
--- a/gi/gerror.c
+++ b/gi/gerror.c
@@ -500,35 +500,15 @@ static void
 define_error_properties(JSContext *context,
                         JSObject  *obj)
 {
-    JSStackFrame *frame;
-    JSScript *script;
-    jsbytecode *pc;
-    jsval v;
-    GString *stack;
-    const char *filename;
-    GjsContext *gjs_context;
     jsid stack_name, filename_name, linenumber_name;
+    jsval stack, fileName, lineNumber;
 
-    /* find the JS frame that triggered the error */
-    frame = NULL;
-    while (JS_FrameIterator(context, &frame)) {
-        script = JS_GetFrameScript(context, frame);
-        if (script)
-            break;
-    }
-
-    /* someone called gjs_throw at top of the stack?
-       well, no stack in that case
-    */
-    if (!frame)
+    if (!gjs_context_get_frame_info (context,
+                                     &stack,
+                                     &fileName,
+                                     &lineNumber))
         return;
 
-    pc = JS_GetFramePC(context, frame);
-
-    stack = g_string_new(NULL);
-    gjs_context = JS_GetContextPrivate(context);
-    gjs_context_print_stack_to_buffer(gjs_context, frame, stack);
-
     stack_name = gjs_runtime_get_const_string(JS_GetRuntime(context),
                                               GJS_STRING_STACK);
     filename_name = gjs_runtime_get_const_string(JS_GetRuntime(context),
@@ -536,20 +516,14 @@ define_error_properties(JSContext *context,
     linenumber_name = gjs_runtime_get_const_string(JS_GetRuntime(context),
                                                    GJS_STRING_LINE_NUMBER);
 
-    if (gjs_string_from_utf8(context, stack->str, stack->len, &v))
-        JS_DefinePropertyById(context, obj, stack_name, v,
+    JS_DefinePropertyById(context, obj, stack_name, stack,
                           NULL, NULL, JSPROP_ENUMERATE);
 
-    filename = JS_GetScriptFilename(context, script);
-    if (gjs_string_from_filename(context, filename, -1, &v))
-        JS_DefinePropertyById(context, obj, filename_name, v,
-                              NULL, NULL, JSPROP_ENUMERATE);
-
-    v = INT_TO_JSVAL(JS_PCToLineNumber(context, script, pc));
-    JS_DefinePropertyById(context, obj, linenumber_name, v,
+    JS_DefinePropertyById(context, obj, filename_name, fileName,
                           NULL, NULL, JSPROP_ENUMERATE);
 
-    g_string_free(stack, TRUE);
+    JS_DefinePropertyById(context, obj, linenumber_name, lineNumber,
+                          NULL, NULL, JSPROP_ENUMERATE);
 }
 
 JSObject*
diff --git a/gjs/context.h b/gjs/context.h
index d19d8a5..cb3f7a5 100644
--- a/gjs/context.h
+++ b/gjs/context.h
@@ -68,13 +68,6 @@ gboolean        gjs_context_define_string_array  (GjsContext  *js_context,
 GList*          gjs_context_get_all              (void);
 void*           gjs_context_get_native_context   (GjsContext *js_context);
 
-/* initial_frame is a JSStackFrame, but cannot be exposed as such in the
-   public API. Pass NULL to get the topmost frame.
-*/
-void            gjs_context_print_stack_to_buffer (GjsContext *js_context,
-                                                   void       *initial_frame,
-                                                   GString    *buf);
-
 void            gjs_context_print_stack_stderr    (GjsContext *js_context);
 
 void            gjs_context_maybe_gc              (GjsContext  *context);
diff --git a/gjs/jsapi-util.h b/gjs/jsapi-util.h
index 6fe1df0..4d21cdf 100644
--- a/gjs/jsapi-util.h
+++ b/gjs/jsapi-util.h
@@ -377,6 +377,11 @@ gboolean gjs_try_block_gc (void);
 void gjs_block_gc (void);
 void gjs_unblock_gc (void);
 
+JSBool            gjs_context_get_frame_info (JSContext  *context,
+                                              jsval      *stack,
+                                              jsval      *fileName,
+                                              jsval      *lineNumber);
+
 G_END_DECLS
 
 #endif  /* __GJS_JSAPI_UTIL_H__ */
diff --git a/gjs/stack.c b/gjs/stack.c
index 06c78fe..afd07f4 100644
--- a/gjs/stack.c
+++ b/gjs/stack.c
@@ -47,200 +47,75 @@
 #include "compat.h"
 #include "jsapi-util.h"
 
-/* Mimick the behaviour exposed by standard Error objects
-   (http://mxr.mozilla.org/mozilla-central/source/js/src/jsexn.cpp#554)
-*/
-static char*
-jsvalue_to_string(JSContext* cx, jsval val, gboolean* is_string)
+JSBool
+gjs_context_get_frame_info (JSContext  *context,
+                            jsval      *stack,
+                            jsval      *fileName,
+                            jsval      *lineNumber)
 {
-    char* value = NULL;
-    JSString* value_str = NULL;
-
-    if (JSVAL_IS_PRIMITIVE(val)) {
-      value_str = JS_ValueToSource(cx, val);
-    } else {
-      JSObject *obj = JSVAL_TO_OBJECT(val);
-
-      if (JS_ObjectIsFunction(cx, obj)) {
-       JSFunction *fn = JS_ValueToFunction(cx, val);
-       value_str = JS_GetFunctionId(fn);
-
-       if (!value_str)
-         value = g_strdup("[unknown function]");
-      } else {
-       value = g_strdup_printf("[object %s]", JS_GetClass(obj)->name);
-      }
+    jsval v_constructor, value;
+    JSObject *err_obj;
+
+    if (!JS_GetProperty(context, JS_GetGlobalObject(context),
+                        "Error", &v_constructor) ||
+        !JSVAL_IS_OBJECT(v_constructor)) {
+        g_error("??? Missing Error constructor in global object?");
+        return JS_FALSE;
     }
 
-    if (!value && value_str)
-      value = gjs_value_debug_string(cx, val);
-
-    if (is_string)
-        *is_string = JSVAL_IS_STRING(val);
-
-    return value;
-}
-
-static void
-format_frame(JSContext* cx, JSStackFrame* fp,
-             GString *buf, int num)
-{
-    JSPropertyDescArray call_props = { 0, NULL };
-    JSObject* call_obj = NULL;
-    char* funname_str = NULL;
-    const char* filename = NULL;
-    guint32 lineno = 0;
-    guint32 named_arg_count = 0;
-    JSFunction* fun = NULL;
-    JSScript* script;
-    guchar* pc;
-    guint32 i;
-    gboolean is_string;
-    jsval val;
-
-    (void)JS_EnterLocalRootScope(cx);
+    err_obj = JS_New(context, JSVAL_TO_OBJECT(v_constructor), 0, NULL);
 
-    /* get the info for this stack frame */
-
-    script = JS_GetFrameScript(cx, fp);
-    pc = JS_GetFramePC(cx, fp);
-
-    if (!script) {
-        g_string_append_printf(buf, "%d [native frame]\n", num);
-        goto out;
+    if (stack != NULL) {
+        if (!gjs_object_get_property_const(context, err_obj,
+                                           GJS_STRING_STACK, &value))
+            return JS_FALSE;
     }
 
-
-    if (script && pc) {
-        filename = JS_GetScriptFilename(cx, script);
-        lineno =  (guint32) JS_PCToLineNumber(cx, script, pc);
-        fun = JS_GetFrameFunction(cx, fp);
-        if (fun) {
-           JSString* funname = JS_GetFunctionId(fun);
-            if (funname)
-              gjs_string_to_utf8(cx, STRING_TO_JSVAL(funname), &funname_str);
-       }
-
-        call_obj = JS_GetFrameCallObject(cx, fp);
-        if (call_obj) {
-            if (!JS_GetPropertyDescArray(cx, call_obj, &call_props))
-                call_props.array = NULL;
-        }
-
+    if (fileName != NULL) {
+        if (!gjs_object_get_property_const(context, err_obj,
+                                           GJS_STRING_FILENAME, &value))
+            return JS_FALSE;
     }
 
-    /* print the frame number and function name */
-
-    if (funname_str) {
-        g_string_append_printf(buf, "%d %s(", num, funname_str);
-        g_free(funname_str);
+    if (lineNumber != NULL) {
+        if (!gjs_object_get_property_const(context, err_obj,
+                                           GJS_STRING_LINE_NUMBER, &value))
+            return JS_FALSE;
     }
-    else if (fun)
-        g_string_append_printf(buf, "%d anonymous(", num);
-    else
-        g_string_append_printf(buf, "%d <TOP LEVEL>", num);
 
-    for (i = 0; i < call_props.length; i++) {
-        char *name = NULL;
-        char *value = NULL;
-        JSPropertyDesc* desc = &call_props.array[i];
-            name = jsvalue_to_string(cx, desc->id, &is_string);
-            if(!is_string) {
-                g_free(name);
-                name = NULL;
-            }
-            value = jsvalue_to_string(cx, desc->value, &is_string);
-
-            g_string_append_printf(buf, "%s%s%s%s%s%s",
-                                   named_arg_count ? ", " : "",
-                                   name ? name :"",
-                                   name ? " = " : "",
-                                   is_string ? "\"" : "",
-                                   value ? value : "?unknown?",
-                                   is_string ? "\"" : "");
-            named_arg_count++;
-        g_free(name);
-        g_free(value);
-    }
-
-    /* print any unnamed trailing args (found in 'arguments' object) */
-
-    if (call_obj != NULL &&
-        JS_GetProperty(cx, call_obj, "arguments", &val) &&
-        JSVAL_IS_OBJECT(val)) {
-        guint32 k;
-        guint32 arg_count;
-        JSObject* args_obj = JSVAL_TO_OBJECT(val);
-        if (JS_GetArrayLength(cx, args_obj, &arg_count) &&
-            arg_count > named_arg_count) {
-            for (k = named_arg_count; k < arg_count; k++) {
-                if (JS_GetElement(cx, args_obj, k, &val)) {
-                    char *value = jsvalue_to_string(cx, val, &is_string);
-                    g_string_append_printf(buf, "%s%s%s%s",
-                                           k ? ", " : "",
-                                           is_string ? "\"" : "",
-                                           value ? value : "?unknown?",
-                                           is_string ? "\"" : "");
-                    g_free(value);
-                }
-            }
-        }
-    }
-
-    /* print filename and line number */
-
-    g_string_append_printf(buf, "%s %s:%d\n",
-                           fun ? ")" : "",
-                           filename ? filename : "",
-                           lineno);
-
-  out:
-    if (call_props.array)
-      JS_PutPropertyDescArray(cx, &call_props);
-
-    JS_LeaveLocalRootScope(cx);
+    return JS_TRUE;
 }
 
 void
-gjs_context_print_stack_to_buffer(GjsContext* context, void *initial, GString *buf)
+gjs_context_print_stack_stderr(GjsContext *context)
 {
-    JSContext *js_context = (JSContext*)gjs_context_get_native_context(context);
-    JSStackFrame *fp = initial;
-    int num = 0;
+    JSContext *cx = gjs_context_get_native_context(context);
+    jsval v_stack;
+    char *stack;
 
-    if (fp == NULL)
-        JS_FrameIterator(js_context, &fp);
+    g_printerr("== Stack trace for context %p ==\n", context);
 
-    while (fp) {
-        format_frame(js_context, fp, buf, num);
-        num++;
-
-       JS_FrameIterator(js_context, &fp);
+    /* Stderr is locale encoding, so we use string_to_filename here */
+    if (!gjs_context_get_frame_info(cx, &v_stack, NULL, NULL) ||
+        !gjs_string_to_filename(cx, v_stack, &stack)) {
+        g_printerr("No stack trace available\n");
+        return;
     }
-}
-
-void
-gjs_context_print_stack_stderr(GjsContext *context)
-{
-  GString *str = g_string_new("");
 
-  g_string_append_printf(str, "== Stack trace for context %p ==\n", context);
-  gjs_context_print_stack_to_buffer(context, NULL, str);
-
-  g_printerr("%s\n", str->str);
-  g_string_free(str, TRUE);
+    g_printerr("%s\n", stack);
+    g_free(stack);
 }
 
 void
 gjs_dumpstack(void)
 {
-  GList *contexts = gjs_context_get_all();
-  GList *iter;
+    GList *contexts = gjs_context_get_all();
+    GList *iter;
 
-  for (iter = contexts; iter; iter = iter->next) {
-    GjsContext *context = (GjsContext*)iter->data;
-    gjs_context_print_stack_stderr(context);
-    g_object_unref(context);
-  }
-  g_list_free(contexts);
+    for (iter = contexts; iter; iter = iter->next) {
+        GjsContext *context = (GjsContext*)iter->data;
+        gjs_context_print_stack_stderr(context);
+        g_object_unref(context);
+    }
+    g_list_free(contexts);
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]