[gjs] GError: add stack, fileName and lineNumber
- From: Giovanni Campagna <gcampagna src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs] GError: add stack, fileName and lineNumber
- Date: Thu, 7 Jun 2012 18:41:50 +0000 (UTC)
commit 52e921dacf553564f6503668bf0be82cfb782d7c
Author: Giovanni Campagna <gcampagna src gnome org>
Date: Sat Mar 3 18:03:14 2012 +0100
GError: add stack, fileName and lineNumber
Similar to native Errors(), GLib.Error is extended to provide debug
information in the form of fileName, lineNumber and stack (obtained
using the JS debug API). At the same time, the existing stack logging
facility is modified to be similar in format to the native one, and
logError is modified to avoid iterating object properties (which
gives an undefined order, and does not include prototype properties)
https://bugzilla.gnome.org/show_bug.cgi?id=591480
gi/arg.c | 4 +-
gi/gerror.c | 63 ++++++++++++++++++++++++++++++++++++++++-
gi/gerror.h | 3 +-
gi/value.c | 2 +-
gjs/context.h | 4 ++
gjs/jsapi-util-error.c | 2 +-
gjs/jsapi-util.c | 36 ++++++++++++-----------
gjs/stack.c | 73 +++++++++++++++++++++++++----------------------
8 files changed, 130 insertions(+), 57 deletions(-)
---
diff --git a/gi/arg.c b/gi/arg.c
index af1750d..8250eb8 100644
--- a/gi/arg.c
+++ b/gi/arg.c
@@ -2379,7 +2379,7 @@ gjs_value_from_g_argument (JSContext *context,
case GI_TYPE_TAG_ERROR:
{
if (arg->v_pointer) {
- JSObject *obj = gjs_error_from_gerror(context, arg->v_pointer);
+ JSObject *obj = gjs_error_from_gerror(context, arg->v_pointer, FALSE);
if (obj) {
*value_p = OBJECT_TO_JSVAL(obj);
return JS_TRUE;
@@ -2459,7 +2459,7 @@ gjs_value_from_g_argument (JSContext *context,
if (g_type_is_a(gtype, G_TYPE_ERROR)) {
JSObject *obj;
- obj = gjs_error_from_gerror(context, arg->v_pointer);
+ obj = gjs_error_from_gerror(context, arg->v_pointer, FALSE);
if (obj)
value = OBJECT_TO_JSVAL(obj);
else
diff --git a/gi/gerror.c b/gi/gerror.c
index 5d50cd5..45dc387 100644
--- a/gi/gerror.c
+++ b/gi/gerror.c
@@ -35,6 +35,7 @@
#include <util/log.h>
#include <jsapi.h>
+#include <jsdbgapi.h>
#include <girepository.h>
@@ -53,6 +54,8 @@ enum {
static struct JSClass gjs_error_class;
+static void define_error_properties(JSContext *, JSObject *);
+
GJS_DEFINE_DYNAMIC_PRIV_FROM_JS(Error, gjs_error_class)
GJS_NATIVE_CONSTRUCTOR_DECLARE(error)
@@ -115,6 +118,9 @@ GJS_NATIVE_CONSTRUCTOR_DECLARE(error)
g_free (message);
+ /* We assume this error will be thrown in the same line as the constructor */
+ define_error_properties(context, object);
+
GJS_NATIVE_CONSTRUCTOR_FINISH(boxed);
return JS_TRUE;
@@ -514,9 +520,61 @@ find_error_domain_info(GQuark domain)
return info;
}
+/* define properties that JS Error() expose, such as
+ fileName, lineNumber and stack
+*/
+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;
+
+ /* find the JS frame that triggered the error */
+ frame = NULL;
+ while (JS_FrameIterator(context, &frame)) {
+ if (JS_IsScriptFrame(context, frame))
+ break;
+ }
+
+ /* someone called gjs_throw at top of the stack?
+ well, no stack in that case
+ */
+ if (!frame)
+ return;
+
+ script = JS_GetFrameScript(context, frame);
+ 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);
+
+ if (gjs_string_from_utf8(context, stack->str, stack->len, &v))
+ JS_DefineProperty(context, obj, "stack", v,
+ NULL, NULL, JSPROP_ENUMERATE);
+
+ filename = JS_GetScriptFilename(context, script);
+ if (gjs_string_from_filename(context, filename, -1, &v))
+ JS_DefineProperty(context, obj, "fileName", v,
+ NULL, NULL, JSPROP_ENUMERATE);
+
+ v = INT_TO_JSVAL(JS_PCToLineNumber(context, script, pc));
+ JS_DefineProperty(context, obj, "lineNumber", v,
+ NULL, NULL, JSPROP_ENUMERATE);
+
+ g_string_free(stack, TRUE);
+}
+
JSObject*
gjs_error_from_gerror(JSContext *context,
- GError *gerror)
+ GError *gerror,
+ gboolean add_stack)
{
JSObject *obj;
JSObject *proto;
@@ -560,6 +618,9 @@ gjs_error_from_gerror(JSContext *context,
g_base_info_ref( (GIBaseInfo*) priv->info);
priv->gerror = g_error_copy(gerror);
+ if (add_stack)
+ define_error_properties(context, obj);
+
return obj;
}
diff --git a/gi/gerror.h b/gi/gerror.h
index 0d93c44..3940d1a 100644
--- a/gi/gerror.h
+++ b/gi/gerror.h
@@ -46,7 +46,8 @@ JSClass* gjs_lookup_error_class (JSContext *context,
GError* gjs_gerror_from_error (JSContext *context,
JSObject *obj);
JSObject* gjs_error_from_gerror (JSContext *context,
- GError *gerror);
+ GError *gerror,
+ gboolean add_stack);
G_END_DECLS
diff --git a/gi/value.c b/gi/value.c
index c64564f..4d02827 100644
--- a/gi/value.c
+++ b/gi/value.c
@@ -675,7 +675,7 @@ gjs_value_from_g_value_internal(JSContext *context,
/* special case GError */
if (g_type_is_a(gtype, G_TYPE_ERROR)) {
- obj = gjs_error_from_gerror(context, gboxed);
+ obj = gjs_error_from_gerror(context, gboxed, FALSE);
*value_p = OBJECT_TO_JSVAL(obj);
return TRUE;
diff --git a/gjs/context.h b/gjs/context.h
index 4e01cc7..d19d8a5 100644
--- a/gjs/context.h
+++ b/gjs/context.h
@@ -68,7 +68,11 @@ 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);
diff --git a/gjs/jsapi-util-error.c b/gjs/jsapi-util-error.c
index 8bce07e..88ac97e 100644
--- a/gjs/jsapi-util-error.c
+++ b/gjs/jsapi-util-error.c
@@ -161,7 +161,7 @@ gjs_throw_g_error (JSContext *context,
JS_BeginRequest(context);
- err_obj = gjs_error_from_gerror(context, error);
+ err_obj = gjs_error_from_gerror(context, error, TRUE);
if (err_obj)
JS_SetPendingException(context, OBJECT_TO_JSVAL(err_obj));
diff --git a/gjs/jsapi-util.c b/gjs/jsapi-util.c
index 6efd528..ca2965e 100644
--- a/gjs/jsapi-util.c
+++ b/gjs/jsapi-util.c
@@ -970,6 +970,21 @@ gjs_explain_scope(JSContext *context,
JS_EndRequest(context);
}
+static void
+log_one_exception_property(JSContext *context,
+ JSObject *object,
+ const char *name)
+{
+ jsval v;
+ char *debugstr;
+
+ gjs_object_get_property(context, object, name, &v);
+
+ debugstr = gjs_value_debug_string(context, v);
+ gjs_debug(GJS_DEBUG_ERROR, " %s = '%s'", name, debugstr);
+ g_free(debugstr);
+}
+
void
gjs_log_exception_props(JSContext *context,
jsval exc)
@@ -988,23 +1003,10 @@ gjs_log_exception_props(JSContext *context,
exc_obj = JSVAL_TO_OBJECT(exc);
- /* I guess this is a SpiderMonkey bug. If we don't get these
- * properties here, only 'message' shows up when we enumerate
- * all properties below. I did not debug in detail, so maybe
- * it's something wrong with our enumeration loop below. In
- * any case, if you remove this code block, check that "throw
- * Error()" still results in printing all four of these props.
- * For me right now, if you remove this block, only message
- * gets printed.
- */
- gjs_object_has_property(context, exc_obj, "stack");
- gjs_object_has_property(context, exc_obj, "fileName");
- gjs_object_has_property(context, exc_obj, "lineNumber");
- gjs_object_has_property(context, exc_obj, "message");
-
- gjs_log_object_props(context, exc_obj,
- GJS_DEBUG_ERROR,
- " ");
+ log_one_exception_property(context, exc_obj, "message");
+ log_one_exception_property(context, exc_obj, "fileName");
+ log_one_exception_property(context, exc_obj, "lineNumber");
+ log_one_exception_property(context, exc_obj, "stack");
} else if (JSVAL_IS_STRING(exc)) {
gjs_debug(GJS_DEBUG_ERROR,
"Exception was a String");
diff --git a/gjs/stack.c b/gjs/stack.c
index 363366d..84e1c6a 100644
--- a/gjs/stack.c
+++ b/gjs/stack.c
@@ -48,34 +48,40 @@
#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)
{
char* value = NULL;
- JSString* value_str;
-
- (void)JS_EnterLocalRootScope(cx);
-
- value_str = JS_ValueToString(cx, val);
- if (value_str)
- value = gjs_value_debug_string(cx, val);
- if (value) {
- const char* found = strstr(value, "function ");
- if(found && (value == found || value+1 == found || value+2 == found)) {
- g_free(value);
- value = g_strdup("[function]");
- }
+ 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(cx, obj)->name);
+ }
}
+ if (!value && value_str)
+ value = gjs_value_debug_string(cx, val);
+
if (is_string)
*is_string = JSVAL_IS_STRING(val);
- JS_LeaveLocalRootScope(cx);
-
return value;
}
-
static void
format_frame(JSContext* cx, JSStackFrame* fp,
GString *buf, int num)
@@ -167,14 +173,10 @@ format_frame(JSContext* cx, JSStackFrame* fp,
guint32 k;
guint32 arg_count;
JSObject* args_obj = JSVAL_TO_OBJECT(val);
- if (JS_GetProperty(cx, args_obj, "length", &val) &&
- JS_ValueToECMAUint32(cx, val, &arg_count) &&
+ if (JS_GetArrayLength(cx, args_obj, &arg_count) &&
arg_count > named_arg_count) {
for (k = named_arg_count; k < arg_count; k++) {
- char number[8];
- g_snprintf(number, 8, "%d", (int) k);
-
- if (JS_GetProperty(cx, args_obj, number, &val)) {
+ 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 ? ", " : "",
@@ -189,38 +191,41 @@ format_frame(JSContext* cx, JSStackFrame* fp,
/* print filename and line number */
- g_string_append_printf(buf, "%s [\"%s\":%d]\n",
+ g_string_append_printf(buf, "%s %s:%d\n",
fun ? ")" : "",
- filename ? filename : "<unknown>",
+ filename ? filename : "",
lineno);
+
out:
+ if (call_props.array)
+ JS_PutPropertyDescArray(cx, &call_props);
+
JS_LeaveLocalRootScope(cx);
}
void
-gjs_context_print_stack_to_buffer(GjsContext* context, GString *buf)
+gjs_context_print_stack_to_buffer(GjsContext* context, void *initial, GString *buf)
{
JSContext *js_context = (JSContext*)gjs_context_get_native_context(context);
- JSStackFrame* fp;
- JSStackFrame* iter = NULL;
+ JSStackFrame* fp = initial;
int num = 0;
- g_string_append_printf(buf, "== Stack trace for context %p ==\n", context);
- while ((fp = JS_FrameIterator(js_context, &iter)) != NULL) {
+ while (fp) {
format_frame(js_context, fp, buf, num);
num++;
- }
- if(!num)
- g_string_append_printf(buf, "(JavaScript stack is empty)\n");
- g_string_append(buf, "\n");
+ JS_FrameIterator(js_context, &fp);
+ }
}
void
gjs_context_print_stack_stderr(GjsContext *context)
{
GString *str = g_string_new("");
- gjs_context_print_stack_to_buffer(context, str);
+
+ 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);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]