Bug 839088 part 1. Add a way to throw a JS::Value on an ErrorResult. r=peterv

This commit is contained in:
Boris Zbarsky 2013-02-19 11:54:40 -05:00
parent e1e9b58bec
commit 25deb6d78c
6 changed files with 94 additions and 3 deletions

View File

@ -119,3 +119,5 @@ DOM4_MSG_DEF(ReadOnlyError, "A mutation operation was attempted in a READ_ONLY l
DOM4_MSG_DEF(InvalidStateError, "A mutation operation was attempted on a file storage that did not allow mutations.", NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR)
DOM4_MSG_DEF(AbortError, "A request was aborted, for example through a call to LockedFile.abort.", NS_ERROR_DOM_FILEHANDLE_ABORT_ERR)
DOM_MSG_DEF(NS_ERROR_DOM_JS_EXCEPTION, "A callback threw an exception")

View File

@ -61,6 +61,14 @@ ErrorResult::ThrowTypeError(const dom::ErrNum errorNumber, ...)
{
va_list ap;
va_start(ap, errorNumber);
if (IsJSException()) {
// We have rooted our mJSException, and we don't have the info
// needed to unroot here, so just bail.
va_end(ap);
MOZ_ASSERT(false,
"Ignoring ThrowTypeError call because we have a JS exception");
return;
}
if (IsTypeError()) {
delete mMessage;
}
@ -107,6 +115,39 @@ ErrorResult::ClearMessage()
}
}
void
ErrorResult::ThrowJSException(JSContext* cx, JS::Value exn)
{
MOZ_ASSERT(mMightHaveUnreportedJSException,
"Why didn't you tell us you planned to throw a JS exception?");
if (IsTypeError()) {
delete mMessage;
}
if (!JS_AddNamedValueRoot(cx, &mJSException, "ErrorResult::mJSException")) {
// Don't use NS_ERROR_DOM_JS_EXCEPTION, because that indicates we have
// in fact rooted mJSException.
mResult = NS_ERROR_OUT_OF_MEMORY;
} else {
mJSException = exn;
mResult = NS_ERROR_DOM_JS_EXCEPTION;
}
}
void
ErrorResult::ReportJSException(JSContext* cx)
{
MOZ_ASSERT(!mMightHaveUnreportedJSException,
"Why didn't you tell us you planned to handle JS exceptions?");
if (JS_WrapValue(cx, &mJSException)) {
JS_SetPendingException(cx, mJSException);
}
// If JS_WrapValue failed, not much we can do about it... No matter
// what, go ahead and unroot mJSException.
JS_RemoveValueRoot(cx, &mJSException);
}
namespace dom {
bool

View File

@ -62,6 +62,10 @@ ThrowMethodFailedWithDetails(JSContext* cx, ErrorResult& rv,
rv.ReportTypeError(cx);
return false;
}
if (rv.IsJSException()) {
rv.ReportJSException(cx);
return false;
}
return Throw<mainThread>(cx, rv.ErrorCode());
}

View File

@ -3686,6 +3686,7 @@ class CGCallGenerator(CGThing):
if isFallible:
self.cgRoot.prepend(CGGeneric("ErrorResult rv;"))
self.cgRoot.append(CGGeneric("rv.WouldReportJSException();"));
self.cgRoot.append(CGGeneric("if (rv.Failed()) {"))
self.cgRoot.append(CGIndenter(errorReport))
self.cgRoot.append(CGGeneric("}"))

View File

@ -35,10 +35,15 @@ class ErrorResult {
public:
ErrorResult() {
mResult = NS_OK;
#ifdef DEBUG
mMightHaveUnreportedJSException = false;
#endif
}
#ifdef DEBUG
~ErrorResult() {
MOZ_ASSERT_IF(IsTypeError(), !mMessage);
MOZ_ASSERT(!mMightHaveUnreportedJSException);
}
#endif
@ -46,6 +51,8 @@ public:
MOZ_ASSERT(NS_FAILED(rv), "Please don't try throwing success");
MOZ_ASSERT(rv != NS_ERROR_TYPE_ERR, "Use ThrowTypeError()");
MOZ_ASSERT(!IsTypeError(), "Don't overwite TypeError");
MOZ_ASSERT(rv != NS_ERROR_DOM_JS_EXCEPTION, "Use ThrowJSException()");
MOZ_ASSERT(!IsJSException(), "Don't overwrite JS exceptions");
mResult = rv;
}
@ -54,6 +61,27 @@ public:
void ClearMessage();
bool IsTypeError() const { return ErrorCode() == NS_ERROR_TYPE_ERR; }
// Facilities for throwing a preexisting JS exception value via this
// ErrorResult. The contract is that any code which might end up calling
// ThrowJSException() must call MightThrowJSException() even if no exception
// is being thrown. Code that would call ReportJSException as needed must
// first call WouldReportJSException even if this ErrorResult has not failed.
void ThrowJSException(JSContext* cx, JS::Value exn);
void ReportJSException(JSContext* cx);
bool IsJSException() const { return ErrorCode() == NS_ERROR_DOM_JS_EXCEPTION; }
void MOZ_ALWAYS_INLINE MightThrowJSException()
{
#ifdef DEBUG
mMightHaveUnreportedJSException = true;
#endif
}
void MOZ_ALWAYS_INLINE WouldReportJSException()
{
#ifdef DEBUG
mMightHaveUnreportedJSException = false;
#endif
}
// In the future, we can add overloads of Throw that take more
// interesting things, like strings or DOM exception types or
// something if desired.
@ -64,6 +92,8 @@ public:
void operator=(nsresult rv) {
MOZ_ASSERT(rv != NS_ERROR_TYPE_ERR, "Use ThrowTypeError()");
MOZ_ASSERT(!IsTypeError(), "Don't overwite TypeError");
MOZ_ASSERT(rv != NS_ERROR_DOM_JS_EXCEPTION, "Use ThrowJSException()");
MOZ_ASSERT(!IsJSException(), "Don't overwrite JS exceptions");
mResult = rv;
}
@ -78,8 +108,20 @@ public:
private:
nsresult mResult;
struct Message;
// Do not use nsAutoPtr to avoid extra initalizatoin and check.
Message* mMessage;
// mMessage is set by ThrowTypeError and cleared (and deallocatd) by
// ReportTypeError.
// mJSException is set (and rooted) by ThrowJSException and unrooted
// by ReportJSException.
union {
Message* mMessage; // valid when IsTypeError()
JS::Value mJSException; // valid when IsJSException()
};
#ifdef DEBUG
// Used to keep track of codepaths that might throw JS exceptions,
// for assertion purposes.
bool mMightHaveUnreportedJSException;
#endif
// Not to be implemented, to make sure people always pass this by
// reference, not by value.

View File

@ -501,7 +501,7 @@
/* ======================================================================= */
#define MODULE NS_ERROR_MODULE_DOM
/* XXX If you add a new DOM error code, also add an error string to
* dom/src/base/domerr.msg */
* dom/base/domerr.msg */
/* Standard DOM error codes: http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html */
ERROR(NS_ERROR_DOM_INDEX_SIZE_ERR, FAILURE(1)),
@ -548,6 +548,7 @@
ERROR(NS_ERROR_DOM_BAD_URI, FAILURE(1012)),
ERROR(NS_ERROR_DOM_RETVAL_UNDEFINED, FAILURE(1013)),
ERROR(NS_ERROR_DOM_QUOTA_REACHED, FAILURE(1014)),
ERROR(NS_ERROR_DOM_JS_EXCEPTION, FAILURE(1015)),
#undef MODULE