Skip to content

Commit c19412a

Browse files
committed
Bug 1224007 part 1. Rename ThrowMethodFailed to MaybeSetPendingException and make it an ErrorResult instance method. r=peterv
1 parent 9f468cf commit c19412a

File tree

7 files changed

+95
-68
lines changed

7 files changed

+95
-68
lines changed

dom/bindings/BindingUtils.cpp

Lines changed: 35 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -125,38 +125,6 @@ ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
125125
NamesOfInterfacesWithProtos(aProtoId));
126126
}
127127

128-
bool
129-
ThrowMethodFailed(JSContext* cx, ErrorResult& rv)
130-
{
131-
if (rv.IsUncatchableException()) {
132-
// Nuke any existing exception on aCx, to make sure we're uncatchable.
133-
JS_ClearPendingException(cx);
134-
// Don't do any reporting. Just return false, to create an
135-
// uncatchable exception.
136-
return false;
137-
}
138-
if (rv.IsJSContextException()) {
139-
// Whatever we need to throw is on the JSContext already. We
140-
// can't assert that there is a pending exception on it, though,
141-
// because in the uncatchable exception case there won't be one.
142-
return false;
143-
}
144-
if (rv.IsErrorWithMessage()) {
145-
rv.ReportErrorWithMessage(cx);
146-
return false;
147-
}
148-
if (rv.IsJSException()) {
149-
rv.ReportJSException(cx);
150-
return false;
151-
}
152-
if (rv.IsDOMException()) {
153-
rv.ReportDOMException(cx);
154-
return false;
155-
}
156-
rv.ReportGenericError(cx);
157-
return false;
158-
}
159-
160128
bool
161129
ThrowNoSetterArg(JSContext* aCx, prototypes::ID aProtoId)
162130
{
@@ -508,6 +476,37 @@ ErrorResult::SuppressException()
508476
mResult = NS_OK;
509477
}
510478

479+
void
480+
ErrorResult::SetPendingException(JSContext* cx)
481+
{
482+
if (IsUncatchableException()) {
483+
// Nuke any existing exception on cx, to make sure we're uncatchable.
484+
JS_ClearPendingException(cx);
485+
// Don't do any reporting. Just return, to create an
486+
// uncatchable exception.
487+
return;
488+
}
489+
if (IsJSContextException()) {
490+
// Whatever we need to throw is on the JSContext already. We
491+
// can't assert that there is a pending exception on it, though,
492+
// because in the uncatchable exception case there won't be one.
493+
return;
494+
}
495+
if (IsErrorWithMessage()) {
496+
ReportErrorWithMessage(cx);
497+
return;
498+
}
499+
if (IsJSException()) {
500+
ReportJSException(cx);
501+
return;
502+
}
503+
if (IsDOMException()) {
504+
ReportDOMException(cx);
505+
return;
506+
}
507+
ReportGenericError(cx);
508+
}
509+
511510
namespace dom {
512511

513512
bool
@@ -2777,10 +2776,10 @@ ConvertExceptionToPromise(JSContext* cx,
27772776
JS_ClearPendingException(cx);
27782777
ErrorResult rv;
27792778
RefPtr<Promise> promise = Promise::Reject(global, exn, rv);
2780-
if (rv.Failed()) {
2781-
// We just give up. Make sure to not leak memory on the
2782-
// ErrorResult, but then just put the original exception back.
2783-
ThrowMethodFailed(cx, rv);
2779+
if (rv.MaybeSetPendingException(cx)) {
2780+
// We just give up. We put the exception from the ErrorResult on
2781+
// the JSContext just to make sure to not leak memory on the
2782+
// ErrorResult, but now just put the original exception back.
27842783
JS_SetPendingException(cx, exn);
27852784
return false;
27862785
}

dom/bindings/BindingUtils.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,6 @@ ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
9292
const ErrNum aErrorNumber,
9393
prototypes::ID aProtoId);
9494

95-
bool
96-
ThrowMethodFailed(JSContext* cx, ErrorResult& rv);
97-
9895
// Returns true if the JSClass is used for DOM objects.
9996
inline bool
10097
IsDOMClass(const JSClass* clasp)

dom/bindings/Codegen.py

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1729,8 +1729,7 @@ def definition_body(self):
17291729
{ // Scope to make sure |result| goes out of scope while |v| is rooted
17301730
RefPtr<mozilla::dom::${descriptorName}> result = ConstructNavigatorObjectHelper(aCx, global, rv);
17311731
rv.WouldReportJSException();
1732-
if (rv.Failed()) {
1733-
ThrowMethodFailed(aCx, rv);
1732+
if (rv.MaybeSetPendingException(aCx)) {
17341733
return nullptr;
17351734
}
17361735
if (!GetOrCreateDOMReflector(aCx, result, &v)) {
@@ -5101,8 +5100,7 @@ def handleNull(templateBody, setToNullVar, extraConditionForNull=""):
51015100
}
51025101
ErrorResult promiseRv;
51035102
$${declName} = Promise::Resolve(promiseGlobal, $${val}, promiseRv);
5104-
if (promiseRv.Failed()) {
5105-
ThrowMethodFailed(cx, promiseRv);
5103+
if (promiseRv.MaybeSetPendingException(cx)) {
51065104
$*{exceptionCode}
51075105
}
51085106
}
@@ -6664,23 +6662,18 @@ class CGCallGenerator(CGThing):
66646662
A class to generate an actual call to a C++ object. Assumes that the C++
66656663
object is stored in a variable whose name is given by the |object| argument.
66666664

6667-
errorReport should be a CGThing for an error report or None if no
6668-
error reporting is needed.
6665+
isFallible is a boolean indicating whether the call should be fallible.
66696666

66706667
resultVar: If the returnType is not void, then the result of the call is
66716668
stored in a C++ variable named by resultVar. The caller is responsible for
66726669
declaring the result variable. If the caller doesn't care about the result
66736670
value, resultVar can be omitted.
66746671
"""
6675-
def __init__(self, errorReport, arguments, argsPre, returnType,
6672+
def __init__(self, isFallible, arguments, argsPre, returnType,
66766673
extendedAttributes, descriptorProvider, nativeMethodName,
66776674
static, object="self", argsPost=[], resultVar=None):
66786675
CGThing.__init__(self)
66796676

6680-
assert errorReport is None or isinstance(errorReport, CGThing)
6681-
6682-
isFallible = errorReport is not None
6683-
66846677
result, resultOutParam, resultRooter, resultArgs, resultConversion = \
66856678
getRetvalDeclarationForType(returnType, descriptorProvider)
66866679

@@ -6775,10 +6768,13 @@ def needsConst(a):
67756768

67766769
if isFallible:
67776770
self.cgRoot.prepend(CGGeneric("ErrorResult rv;\n"))
6778-
self.cgRoot.append(CGGeneric("rv.WouldReportJSException();\n"))
6779-
self.cgRoot.append(CGGeneric("if (MOZ_UNLIKELY(rv.Failed())) {\n"))
6780-
self.cgRoot.append(CGIndenter(errorReport))
6781-
self.cgRoot.append(CGGeneric("}\n"))
6771+
self.cgRoot.append(CGGeneric(dedent(
6772+
"""
6773+
rv.WouldReportJSException();
6774+
if (MOZ_UNLIKELY(rv.MaybeSetPendingException(cx))) {
6775+
return false;
6776+
}
6777+
""")))
67826778

67836779
self.cgRoot.append(CGGeneric("MOZ_ASSERT(!JS_IsExceptionPending(cx));\n"))
67846780

@@ -7172,7 +7168,7 @@ def __init__(self, returnType, arguments, nativeMethodName, static,
71727168
idlNode.identifier.name))
71737169
else:
71747170
cgThings.append(CGCallGenerator(
7175-
self.getErrorReport() if self.isFallible() else None,
7171+
self.isFallible(),
71767172
self.getArguments(), argsPre, returnType,
71777173
self.extendedAttributes, descriptor, nativeMethodName,
71787174
static, argsPost=argsPost, resultVar=resultVar))
@@ -7279,9 +7275,6 @@ def wrap_return_value(self):
72797275
maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type))
72807276
return wrapCode
72817277

7282-
def getErrorReport(self):
7283-
return CGGeneric('return ThrowMethodFailed(cx, rv);\n')
7284-
72857278
def define(self):
72867279
return (self.cgRoot.define() + self.wrap_return_value())
72877280

@@ -8251,8 +8244,8 @@ def generate_code(self):
82518244
ErrorResult rv;
82528245
self->GetOwnPropertyNames(cx, names, rv);
82538246
rv.WouldReportJSException();
8254-
if (rv.Failed()) {
8255-
return ThrowMethodFailed(cx, rv);
8247+
if (rv.MaybeSetPendingException(cx)) {
8248+
return false;
82568249
}
82578250
bool dummy;
82588251
for (uint32_t i = 0; i < names.Length(); ++i) {
@@ -10309,8 +10302,8 @@ def generate_code(self):
1030910302
ErrorResult rv;
1031010303
self->GetOwnPropertyNames(cx, names, rv);
1031110304
rv.WouldReportJSException();
10312-
if (rv.Failed()) {
10313-
return ThrowMethodFailed(cx, rv);
10305+
if (rv.MaybeSetPendingException(cx)) {
10306+
return false;
1031410307
}
1031510308
// OK to pass null as "proxy" because it's ignored if
1031610309
// shadowPrototypeProperties is true

dom/bindings/ErrorResult.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,40 @@ class ErrorResult {
134134
return rv;
135135
}
136136

137+
// Use MaybeSetPendingException to convert an ErrorResult to a pending
138+
// exception on the given JSContext. This is the normal "throw an exception"
139+
// codepath.
140+
//
141+
// The return value is false if the ErrorResult represents success, true
142+
// otherwise. This does mean that in JSAPI method implementations you can't
143+
// just use this as |return rv.MaybeSetPendingException(cx)| (though you could
144+
// |return !rv.MaybeSetPendingException(cx)|), but in practice pretty much any
145+
// consumer would want to do some more work on the success codepath. So
146+
// instead the way you use this is:
147+
//
148+
// if (rv.MaybeSetPendingException(cx)) {
149+
// bail out here
150+
// }
151+
// go on to do something useful
152+
//
153+
// The success path is inline, since it should be the common case and we don't
154+
// want to pay the price of a function call in some of the consumers of this
155+
// method in the common case.
156+
//
157+
// Note that a true return value does NOT mean there is now a pending
158+
// exception on aCx, due to uncatchable exceptions. It should still be
159+
// considered equivalent to a JSAPI failure in terms of what callers should do
160+
// after true is returned.
161+
bool MaybeSetPendingException(JSContext* cx)
162+
{
163+
if (!Failed()) {
164+
return false;
165+
}
166+
167+
SetPendingException(cx);
168+
return true;
169+
}
170+
137171
template<dom::ErrNum errorNumber, typename... Ts>
138172
void ThrowTypeError(Ts&&... messageArgs)
139173
{
@@ -309,6 +343,10 @@ class ErrorResult {
309343
// anymore.
310344
void ClearUnionData();
311345

346+
// Implementation of MaybeSetPendingException for the case when we're a
347+
// failure result.
348+
void SetPendingException(JSContext* cx);
349+
312350
// Special values of mResult:
313351
// NS_ERROR_TYPE_ERR -- ThrowTypeError() called on us.
314352
// NS_ERROR_RANGE_ERR -- ThrowRangeError() called on us.

dom/bindings/ToJSValue.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ ToJSValue(JSContext* aCx,
5656
MOZ_ASSERT(!aArgument.IsUncatchableException(),
5757
"Doesn't make sense to convert uncatchable exception to a JS value!");
5858
AutoForceSetExceptionOnContext forceExn(aCx);
59-
DebugOnly<bool> throwResult = ThrowMethodFailed(aCx, aArgument);
60-
MOZ_ASSERT(!throwResult);
59+
DebugOnly<bool> throwResult = aArgument.MaybeSetPendingException(aCx);
60+
MOZ_ASSERT(throwResult);
6161
DebugOnly<bool> getPendingResult = JS_GetPendingException(aCx, aValue);
6262
MOZ_ASSERT(getPendingResult);
6363
JS_ClearPendingException(aCx);

dom/cache/CacheStorage.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,8 +261,8 @@ CacheStorage::DefineCaches(JSContext* aCx, JS::Handle<JSObject*> aGlobal)
261261
false, /* private browsing */
262262
true, /* force trusted */
263263
rv);
264-
if (NS_WARN_IF(rv.Failed())) {
265-
return ThrowMethodFailed(aCx, rv);
264+
if (NS_WARN_IF(rv.MaybeSetPendingException(aCx))) {
265+
return false;
266266
}
267267

268268
JS::Rooted<JS::Value> caches(aCx);

js/xpconnect/src/Sandbox.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,8 +290,8 @@ SandboxFetch(JSContext* cx, JS::HandleObject scope, const CallArgs& args)
290290
RefPtr<dom::Promise> response =
291291
FetchRequest(global, Constify(request), Constify(options), rv);
292292
rv.WouldReportJSException();
293-
if (rv.Failed()) {
294-
return ThrowMethodFailed(cx, rv);
293+
if (rv.MaybeSetPendingException(cx)) {
294+
return false;
295295
}
296296
if (!GetOrCreateDOMReflector(cx, response, args.rval())) {
297297
return false;

0 commit comments

Comments
 (0)