COUCHDB-111 and COUCHDB-1389 JS Error Tracebacks couchjs: - report stacktraces on exceptions using JS_ReportError
- responds with a trace and message on errors when possible - propogate Error-like objects from validate_doc_update to the loop - make Error-like object play nicely with couch_os_process couch.js: - transform HTTP error response bodies into an Error instance to capture stack information cli tests: - print a stacktrace for individual test failures when running the suite Fix COUCHDB-111 Fix COUCHDB-1389 Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/32a11134 Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/32a11134 Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/32a11134 Branch: refs/heads/master Commit: 32a1113417c25deee9052e7d3de37cc0faea9914 Parents: 5f55e9f Author: Randall Leeds <[email protected]> Authored: Thu Dec 15 19:49:34 2011 -0800 Committer: Randall Leeds <[email protected]> Committed: Thu Jan 26 17:02:38 2012 -0800 ---------------------------------------------------------------------- share/server/loop.js | 2 + share/server/validate.js | 5 +++- share/www/script/couch.js | 13 ++++++++- src/couchdb/priv/couch_js/sm170.c | 2 + src/couchdb/priv/couch_js/sm180.c | 2 + src/couchdb/priv/couch_js/sm185.c | 2 + src/couchdb/priv/couch_js/util.c | 45 +++++++++++++++++++++++++++++++- test/javascript/cli_runner.js | 19 ++++++------- 8 files changed, 77 insertions(+), 13 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/couchdb/blob/32a11134/share/server/loop.js ---------------------------------------------------------------------- diff --git a/share/server/loop.js b/share/server/loop.js index eb7f75b..986c8b3 100644 --- a/share/server/loop.js +++ b/share/server/loop.js @@ -140,6 +140,8 @@ var Loop = function() { } else if (e.error && e.reason) { // compatibility with old error format respond(["error", e.error, e.reason]); + } else if (e.name) { + respond(["error", e.name, e]); } else { respond(["error","unnamed_error",e.toSource()]); } http://git-wip-us.apache.org/repos/asf/couchdb/blob/32a11134/share/server/validate.js ---------------------------------------------------------------------- diff --git a/share/server/validate.js b/share/server/validate.js index 76a1412..5b50e54 100644 --- a/share/server/validate.js +++ b/share/server/validate.js @@ -14,8 +14,11 @@ var Validate = { validate : function(fun, ddoc, args) { try { fun.apply(ddoc, args); - print("1"); + respond(1); } catch (error) { + if (error.name && error.stack) { + throw error; + } respond(error); } } http://git-wip-us.apache.org/repos/asf/couchdb/blob/32a11134/share/www/script/couch.js ---------------------------------------------------------------------- diff --git a/share/www/script/couch.js b/share/www/script/couch.js index 86aaabf..d078d96 100644 --- a/share/www/script/couch.js +++ b/share/www/script/couch.js @@ -462,7 +462,8 @@ CouchDB.maybeThrowError = function(req) { } catch (ParseError) { var result = {error:"unknown", reason:req.responseText}; } - throw result; + + throw (new CouchError(result)); } } @@ -485,3 +486,13 @@ if (typeof window == 'undefined' || !window) { CouchDB.inBrowser = true; CouchDB.protocol = window.location.protocol + "//"; } + +// Turns an {error: ..., reason: ...} response into an Error instance +function CouchError(error) { + var inst = new Error(error.reason); + inst.name = 'CouchError'; + inst.error = error.error; + inst.reason = error.reason; + return inst; +} +CouchError.prototype.constructor = CouchError; http://git-wip-us.apache.org/repos/asf/couchdb/blob/32a11134/src/couchdb/priv/couch_js/sm170.c ---------------------------------------------------------------------- diff --git a/src/couchdb/priv/couch_js/sm170.c b/src/couchdb/priv/couch_js/sm170.c index 796c1d6..bb28870 100644 --- a/src/couchdb/priv/couch_js/sm170.c +++ b/src/couchdb/priv/couch_js/sm170.c @@ -113,6 +113,7 @@ req_status(JSContext* cx, JSObject* obj, jsval idval, jsval* rval) static JSBool +base_url(JSContext *cx, JSObject* obj, jsval idval, jsval* rval) evalcx(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; @@ -244,6 +245,7 @@ JSClass CouchHTTPClass = { JSPropertySpec CouchHTTPProperties[] = { {"status", 0, JSPROP_READONLY, req_status, NULL}, + {"base_url", 0, JSPROP_READONLY | JSPROP_SHARED, base_url, NULL}, {0, 0, 0, 0, 0} }; http://git-wip-us.apache.org/repos/asf/couchdb/blob/32a11134/src/couchdb/priv/couch_js/sm180.c ---------------------------------------------------------------------- diff --git a/src/couchdb/priv/couch_js/sm180.c b/src/couchdb/priv/couch_js/sm180.c index 4d1bbf9..d7728a3 100644 --- a/src/couchdb/priv/couch_js/sm180.c +++ b/src/couchdb/priv/couch_js/sm180.c @@ -116,6 +116,7 @@ req_status(JSContext* cx, JSObject* obj, jsval idval, jsval* vp) static JSBool +base_url(JSContext *cx, JSObject* obj, jsid pid, jsval* vp) evalcx(JSContext *cx, uintN argc, jsval* vp) { jsval* argv = JS_ARGV(cx, vp); @@ -253,6 +254,7 @@ JSClass CouchHTTPClass = { JSPropertySpec CouchHTTPProperties[] = { {"status", 0, JSPROP_READONLY, req_status, NULL}, + {"base_url", 0, JSPROP_READONLY | JSPROP_SHARED, base_url, NULL}, {0, 0, 0, 0, 0} }; http://git-wip-us.apache.org/repos/asf/couchdb/blob/32a11134/src/couchdb/priv/couch_js/sm185.c ---------------------------------------------------------------------- diff --git a/src/couchdb/priv/couch_js/sm185.c b/src/couchdb/priv/couch_js/sm185.c index 8c4e536..7757514 100644 --- a/src/couchdb/priv/couch_js/sm185.c +++ b/src/couchdb/priv/couch_js/sm185.c @@ -134,6 +134,7 @@ req_status(JSContext* cx, JSObject* obj, jsid pid, jsval* vp) static JSBool +base_url(JSContext *cx, JSObject* obj, jsid pid, jsval* vp) evalcx(JSContext *cx, uintN argc, jsval* vp) { jsval* argv = JS_ARGV(cx, vp); @@ -279,6 +280,7 @@ JSClass CouchHTTPClass = { JSPropertySpec CouchHTTPProperties[] = { {"status", 0, JSPROP_READONLY, req_status, NULL}, + {"base_url", 0, JSPROP_READONLY | JSPROP_SHARED, base_url, NULL}, {0, 0, 0, 0, 0} }; http://git-wip-us.apache.org/repos/asf/couchdb/blob/32a11134/src/couchdb/priv/couch_js/util.c ---------------------------------------------------------------------- diff --git a/src/couchdb/priv/couch_js/util.c b/src/couchdb/priv/couch_js/util.c index 0b1e92a..b7bd3e4 100644 --- a/src/couchdb/priv/couch_js/util.c +++ b/src/couchdb/priv/couch_js/util.c @@ -227,9 +227,52 @@ couch_print(JSContext* cx, uintN argc, jsval* argv) void couch_error(JSContext* cx, const char* mesg, JSErrorReport* report) { + jsval v, replace; + char* bytes; + JSObject* regexp, *stack; + jsval re_args[2]; + if(!report || !JSREPORT_IS_WARNING(report->flags)) { - fprintf(stderr, "[couchjs] %s\n", mesg); + fprintf(stderr, "%s\n", mesg); + + // Print a stack trace, if available. + if (JSREPORT_IS_EXCEPTION(report->flags) && + JS_GetPendingException(cx, &v)) + { + // Clear the exception before an JS method calls or the result is + // infinite, recursive error report generation. + JS_ClearPendingException(cx); + + // Use JS regexp to indent the stack trace. + // If the regexp can't be created, don't JS_ReportError since it is + // probably not productive to wind up here again. +#ifdef SM185 + if(JS_GetProperty(cx, JSVAL_TO_OBJECT(v), "stack", &v) && + (regexp = JS_NewRegExpObjectNoStatics( + cx, "^(?=.)", 6, JSREG_GLOB | JSREG_MULTILINE))) +#else + if(JS_GetProperty(cx, JSVAL_TO_OBJECT(v), "stack", &v) && + (regexp = JS_NewRegExpObject( + cx, "^(?=.)", 6, JSREG_GLOB | JSREG_MULTILINE))) +#endif + { + // Set up the arguments to ``String.replace()`` + re_args[0] = OBJECT_TO_JSVAL(regexp); + re_args[1] = STRING_TO_JSVAL(JS_InternString(cx, "\t")); + + // Perform the replacement + if(JS_ValueToObject(cx, v, &stack) && + JS_GetProperty(cx, stack, "replace", &replace) && + JS_CallFunctionValue(cx, stack, replace, 2, re_args, &v)) + { + // Print the result + bytes = enc_string(cx, v, NULL); + fprintf(stderr, "Stacktrace:\n%s", bytes); + JS_free(cx, bytes); + } + } + } } } http://git-wip-us.apache.org/repos/asf/couchdb/blob/32a11134/test/javascript/cli_runner.js ---------------------------------------------------------------------- diff --git a/test/javascript/cli_runner.js b/test/javascript/cli_runner.js index 6683ede..d9d7fce 100644 --- a/test/javascript/cli_runner.js +++ b/test/javascript/cli_runner.js @@ -19,7 +19,8 @@ var console = { function T(arg1, arg2) { if(!arg1) { - throw((arg2 ? arg2 : arg1).toString()); + var result = (arg2 ? arg2 : arg1); + throw((result instanceof Error ? result : Error(result))); } } @@ -28,9 +29,11 @@ function runTestConsole(num, name, func) { func(); print("ok " + num + " " + name); } catch(e) { - msg = e.toString(); - msg = msg.replace(/\n/g, "\n "); - print("not ok " + num + " " + name + " " + msg); + print("not ok " + num + " " + name); + print("# " + e.toSource()); + if (e.stack) { + print("# Stacktrace:\n" + e.stack.replace(/^/gm, "\t")); + } } } @@ -45,9 +48,5 @@ function runAllTestsConsole() { } }; -try { - waitForSuccess(CouchDB.getVersion); - runAllTestsConsole(); -} catch (e) { - p("# " + e.toString()); -} +waitForSuccess(CouchDB.getVersion); +runAllTestsConsole();
