diff options
author | Randall Leeds <randall@apache.org> | 2011-12-15 19:49:34 -0800 |
---|---|---|
committer | Randall Leeds <randall@apache.org> | 2012-01-26 17:02:38 -0800 |
commit | 32a1113417c25deee9052e7d3de37cc0faea9914 (patch) | |
tree | 2e785fa8e004b5b588fd8293627adc3a92c33f6a | |
parent | 5f55e9f18397b671cefcf1ce8eef1b787f36c398 (diff) | |
download | couchdb-32a1113417c25deee9052e7d3de37cc0faea9914.tar.gz |
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
-rw-r--r-- | share/server/loop.js | 2 | ||||
-rw-r--r-- | share/server/validate.js | 5 | ||||
-rw-r--r-- | share/www/script/couch.js | 13 | ||||
-rw-r--r-- | src/couchdb/priv/couch_js/sm170.c | 2 | ||||
-rw-r--r-- | src/couchdb/priv/couch_js/sm180.c | 2 | ||||
-rw-r--r-- | src/couchdb/priv/couch_js/sm185.c | 2 | ||||
-rw-r--r-- | src/couchdb/priv/couch_js/util.c | 45 | ||||
-rw-r--r-- | test/javascript/cli_runner.js | 19 |
8 files changed, 77 insertions, 13 deletions
diff --git a/share/server/loop.js b/share/server/loop.js index eb7f75b3e..986c8b3f8 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()]); } diff --git a/share/server/validate.js b/share/server/validate.js index 76a141299..5b50e5473 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); } } diff --git a/share/www/script/couch.js b/share/www/script/couch.js index 86aaabff8..d078d9647 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; diff --git a/src/couchdb/priv/couch_js/sm170.c b/src/couchdb/priv/couch_js/sm170.c index 796c1d6b5..bb28870d7 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} }; diff --git a/src/couchdb/priv/couch_js/sm180.c b/src/couchdb/priv/couch_js/sm180.c index 4d1bbf9a8..d7728a338 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} }; diff --git a/src/couchdb/priv/couch_js/sm185.c b/src/couchdb/priv/couch_js/sm185.c index 8c4e536f8..7757514ce 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} }; diff --git a/src/couchdb/priv/couch_js/util.c b/src/couchdb/priv/couch_js/util.c index 0b1e92a37..b7bd3e41e 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); + } + } + } } } diff --git a/test/javascript/cli_runner.js b/test/javascript/cli_runner.js index 6683edef9..d9d7fce1b 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(); |