summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Leeds <randall@apache.org>2011-12-15 19:49:34 -0800
committerRandall Leeds <randall@apache.org>2012-01-26 17:02:38 -0800
commit32a1113417c25deee9052e7d3de37cc0faea9914 (patch)
tree2e785fa8e004b5b588fd8293627adc3a92c33f6a
parent5f55e9f18397b671cefcf1ce8eef1b787f36c398 (diff)
downloadcouchdb-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.js2
-rw-r--r--share/server/validate.js5
-rw-r--r--share/www/script/couch.js13
-rw-r--r--src/couchdb/priv/couch_js/sm170.c2
-rw-r--r--src/couchdb/priv/couch_js/sm180.c2
-rw-r--r--src/couchdb/priv/couch_js/sm185.c2
-rw-r--r--src/couchdb/priv/couch_js/util.c45
-rw-r--r--test/javascript/cli_runner.js19
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();