Add initial support for Debug.prototype.uncaughtExceptionHook.

This commit is contained in:
Jason Orendorff 2011-04-19 22:19:38 -05:00
parent f98bcedaf3
commit 49262897d7
6 changed files with 150 additions and 16 deletions

View File

@ -115,25 +115,39 @@ CheckThisClass(JSContext *cx, Value *vp, Class *clasp, const char *fnname)
Debug::Debug(JSObject *dbg, JSObject *hooks, JSCompartment *compartment)
: object(dbg), debuggeeCompartment(compartment), hooksObject(hooks),
enabled(true), hasDebuggerHandler(false)
uncaughtExceptionHook(NULL), enabled(true), hasDebuggerHandler(false)
{
}
JSTrapStatus
Debug::fireUncaughtExceptionHook(JSContext *cx)
Debug::handleUncaughtException(AutoCompartment &ac, Value *vp, bool callHook)
{
// FIXME
JSContext *cx = ac.context;
if (cx->isExceptionPending()) {
if (callHook && uncaughtExceptionHook) {
Value fval = ObjectValue(*uncaughtExceptionHook);
Value exc = cx->getPendingException();
Value rv;
cx->clearPendingException();
if (ExternalInvoke(cx, UndefinedValue(), fval, 1, &exc, &rv))
return parseResumptionValue(ac, true, rv, vp, false);
}
if (cx->isExceptionPending()) {
JS_ReportPendingException(cx);
JS_ClearPendingException(cx);
cx->clearPendingException();
}
}
return JSTRAP_ERROR;
}
JSTrapStatus
Debug::parseResumptionValue(AutoCompartment &ac, bool ok, const Value &rv, Value *vp)
Debug::parseResumptionValue(AutoCompartment &ac, bool ok, const Value &rv, Value *vp,
bool callHook)
{
vp->setUndefined();
if (!ok)
return fireUncaughtExceptionHook(ac.context);
return handleUncaughtException(ac, vp, callHook);
if (rv.isUndefined())
return JSTRAP_CONTINUE;
if (rv.isNull())
@ -153,18 +167,18 @@ Debug::parseResumptionValue(AutoCompartment &ac, bool ok, const Value &rv, Value
!shape->isDataDescriptor())
{
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_BAD_RESUMPTION);
return fireUncaughtExceptionHook(cx);
return handleUncaughtException(ac, vp, callHook);
}
if (!js_NativeGet(cx, obj, obj, shape, 0, vp))
return fireUncaughtExceptionHook(cx);
return handleUncaughtException(ac, vp, callHook);
// Throwing or returning objects is not yet supported. It requires
// unwrapping.
if (vp->isObject()) {
vp->setUndefined();
NotImplemented(cx);
return fireUncaughtExceptionHook(cx);
return handleUncaughtException(ac, vp, callHook);
}
ac.leave();
@ -281,8 +295,9 @@ void
Debug::trace(JSTracer *trc, JSObject *obj)
{
if (Debug *dbg = (Debug *) obj->getPrivate()) {
if (dbg->hooksObject)
MarkObject(trc, *dbg->hooksObject, "hooks");
if (dbg->uncaughtExceptionHook)
MarkObject(trc, *dbg->uncaughtExceptionHook, "hooks");
}
}
@ -325,7 +340,7 @@ JSBool
Debug::getHooks(JSContext *cx, uintN argc, Value *vp)
{
THISOBJ(cx, vp, Debug, "get hooks", thisobj, dbg);
vp->setObjectOrNull(dbg->hooksObject);
vp->setObject(*dbg->hooksObject);
return true;
}
@ -366,6 +381,26 @@ Debug::setEnabled(JSContext *cx, uintN argc, Value *vp)
return true;
}
JSBool
Debug::getUncaughtExceptionHook(JSContext *cx, uintN argc, Value *vp)
{
THISOBJ(cx, vp, Debug, "get uncaughtExceptionHook", thisobj, dbg);
vp->setObjectOrNull(dbg->uncaughtExceptionHook);
return true;
}
JSBool
Debug::setUncaughtExceptionHook(JSContext *cx, uintN argc, Value *vp)
{
REQUIRE_ARGC("Debug.set uncaughtExceptionHook", 1);
THISOBJ(cx, vp, Debug, "set uncaughtExceptionHook", thisobj, dbg);
if (!vp[2].isObjectOrNull())
return ReportObjectRequired(cx);
dbg->uncaughtExceptionHook = vp[2].toObjectOrNull();
vp->setUndefined();
return true;
}
JSBool
Debug::construct(JSContext *cx, uintN argc, Value *vp)
{
@ -415,6 +450,8 @@ Debug::construct(JSContext *cx, uintN argc, Value *vp)
JSPropertySpec Debug::properties[] = {
JS_PSGS("hooks", Debug::getHooks, Debug::setHooks, 0),
JS_PSGS("enabled", Debug::getEnabled, Debug::setEnabled, 0),
JS_PSGS("uncaughtExceptionHook", Debug::getUncaughtExceptionHook,
Debug::setUncaughtExceptionHook, 0),
JS_PS_END
};

View File

@ -56,15 +56,16 @@ class Debug {
JSObject *object; // The Debug object. Strong reference.
JSCompartment *debuggeeCompartment; // Weak reference.
JSObject *hooksObject; // See Debug.prototype.hooks. Strong reference.
JSObject *uncaughtExceptionHook; // Strong reference.
bool enabled;
// True if hooksObject had a debuggerHandler property when the hooks
// property was set.
bool hasDebuggerHandler;
JSTrapStatus fireUncaughtExceptionHook(JSContext *cx);
JSTrapStatus parseResumptionValue(AutoCompartment &ac, bool ok, const Value &rv, Value *vp);
JSTrapStatus handleUncaughtException(AutoCompartment &ac, Value *vp, bool callHook);
JSTrapStatus parseResumptionValue(AutoCompartment &ac, bool ok, const Value &rv, Value *vp,
bool callHook = true);
static void trace(JSTracer *trc, JSObject *obj);
static void finalize(JSContext *cx, JSObject *obj);
@ -74,6 +75,8 @@ class Debug {
static JSBool setHooks(JSContext *cx, uintN argc, Value *vp);
static JSBool getEnabled(JSContext *cx, uintN argc, Value *vp);
static JSBool setEnabled(JSContext *cx, uintN argc, Value *vp);
static JSBool getUncaughtExceptionHook(JSContext *cx, uintN argc, Value *vp);
static JSBool setUncaughtExceptionHook(JSContext *cx, uintN argc, Value *vp);
static JSBool construct(JSContext *cx, uintN argc, Value *vp);
static JSPropertySpec properties[];

View File

@ -0,0 +1,25 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
// Uncaught exceptions in the debugger itself are delivered to the
// uncaughtExceptionHook.
var g = newGlobal('new-compartment');
var dbg = new Debug(g);
var log;
dbg.hooks = {
debuggerHandler: function () {
log += 'x';
throw new TypeError("fail");
}
};
dbg.uncaughtExceptionHook = function (exc) {
assertEq(exc instanceof TypeError, true);
log += '!';
};
log = '';
g.eval("debugger");
assertEq(log, 'x!');
reportCompare(0, 0, 'ok');

View File

@ -0,0 +1,34 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
// uncaughtExceptionHook returns a resumption value.
var g = newGlobal('new-compartment');
var dbg = new Debug(g);
var rv;
dbg.hooks = {debuggerHandler: function () { throw 15; }};
dbg.uncaughtExceptionHook = function (exc) {
assertEq(exc, 15);
return rv;
};
// case 1: undefined
rv = undefined;
g.eval("debugger");
// case 2: throw
rv = {throw: 57};
var result;
try {
g.eval("debugger");
result = 'no exception thrown';
} catch (exc) {
result = 'caught ' + exc;
}
assertEq(result, 'caught 57');
// case 3: return
rv = {return: 42};
assertEq(g.eval("debugger;"), 42);
reportCompare(0, 0, 'ok');

View File

@ -0,0 +1,32 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
// uncaughtExceptionHook resumption value other than undefined causes further
// hooks to be skipped.
var g = newGlobal('new-compartment');
var log;
function makeDebug(g, name) {
var dbg = new Debug(g);
dbg.hooks = {
debuggerHandler: function () {
log += name;
throw new Error(name);
}
};
dbg.uncaughtExceptionHook = function (exc) {
assertEq(exc.message, name);
return name == "2" ? {return: 42} : undefined;
};
}
var arr = [];
for (var i = 0; i < 6; i++)
arr[i] = makeDebug(g, "" + i);
log = '';
assertEq(g.eval("debugger;"), 42);
assertEq(log, "012");
reportCompare(0, 0, 'ok');

View File

@ -58,3 +58,6 @@ skip-if(!xulRuntime.shell) script debug-object-14.js
skip-if(!xulRuntime.shell) script debug-object-15.js
skip-if(!xulRuntime.shell) script debug-object-16.js
skip-if(!xulRuntime.shell) script debug-object-17.js
skip-if(!xulRuntime.shell) script debug-object-18.js
skip-if(!xulRuntime.shell) script debug-object-19.js
skip-if(!xulRuntime.shell) script debug-object-20.js