Closed Bug 853548 Opened 12 years ago Closed 12 years ago

Make nsTArray::SetCapacity return void

Categories

(Core :: XPCOM, defect)

x86
macOS
defect
Not set
normal

Tracking

()

RESOLVED FIXED
mozilla22

People

(Reporter: ehsan.akhgari, Assigned: ehsan.akhgari)

References

Details

Attachments

(1 file, 1 obsolete file)

Currently it returns bool, so if you have code which calls SetCapacity and tries to deal with false return values but say nsTArray when you really mean FallibleTArray, we don't catch that at compile time, and then we end up with bugs like bug 852838. I have a patch to fix this, and catch other similar cases in the tree.
Depends on: 853554
Assignee: nobody → ehsan
Depends on: 853556
Depends on: 853560
Depends on: 853562
Depends on: 853567
Depends on: 853569
Depends on: 853572
Attached patch Patch (v1) (obsolete) — Splinter Review
Attachment #727808 - Flags: review?(justin.lebar+bug)
We have code in nsTArray to allow const nsTArray& and const nsFallibleTArray& to be cast between each other. I suspect this will mess that up very badly. I don't think we can have both.
(In reply to comment #2) > We have code in nsTArray to allow const nsTArray& and const nsFallibleTArray& > to be cast between each other. > > I suspect this will mess that up very badly. I don't think we can have both. Why do we want to allow such a cast?
Because we want to be able to pass "anything" to a method which takes const nsTArray&. We do this in a few places in our code (I suppose you could find them by removing the cast operators).
(In reply to comment #4) > Because we want to be able to pass "anything" to a method which takes const > nsTArray&. We do this in a few places in our code (I suppose you could find > them by removing the cast operators). Hmm, I see. But then again, if the cast operator only casts between const references, SetCapacity cannot be called on those casted objects, right?
The cast operator definitely only works on const references, and the assumption is that callees do not cast away const and try to mutate the array... Justin, what cases do you think would break with this change?
> SetCapacity cannot be called on those casted objects, right? Oh, duh. Sorry. If we only change non-const methods in this way, it should be fine.
Yeah, I would expect only non-const methods attempt to allocate/reallocate. If not, then we should mark those methods as non-const. BTW, this is a bit of black magic C++. I have added some comments to the darkest trick that I used here, the rest is not that complicated. Fortunately this infrastructure can later be used to do the same thing for other nsTArray methods which are really infallible and always return true. (And so far, it has found another footgun: bug 853554.) Justin, please let me know if you think I can add more comments to make this easier to understand.
Depends on: 854108
Comment on attachment 727808 [details] [diff] [review] Patch (v1) This looks good to me, with my |const| misunderstanding out of the way. Just some nits. >@@ -92,44 +92,44 @@ bool nsTArray_base<Alloc>::UsesAutoArrayBuffer() const { > NS_ABORT_IF_FALSE(diff >= 0 && diff <= 4, "GetAutoArrayBuffer doesn't do what we expect."); > #endif > > return mHdr == GetAutoArrayBuffer(4) || mHdr == GetAutoArrayBuffer(8); > } > > > template<class Alloc> >-bool >+typename Alloc::SuccessResultProxy > nsTArray_base<Alloc>::EnsureCapacity(size_type capacity, size_type elemSize) { Ideally we want the fallible TArray functions to emit a compile warning if their return results are unused. It's not clear to me how to do so without abusing macros; do you have an idea for accomplishing that? We don't have to do it here, of course. >diff --git a/xpcom/glue/nsTArray.h b/xpcom/glue/nsTArray.h >index 1f20c31..527483c 100644 >--- a/xpcom/glue/nsTArray.h >+++ b/xpcom/glue/nsTArray.h >+// nsTArrayFallibleSuccessResult and nsTArrayInfallibleSuccess results are >+// proxy types which are used because you cannot use a templated type which >+// is bound to void as an argument to a void function. In order to work >+// around that, we encode either a void or a boolean inside these proxy >+// objects, and pass them to the aforementioned function instead, and then >+// use the type information to decide what to do in the function. So I should return AllocSuccessProxy from internal TArray methods, and I should return AllocSuccess from public methods? And I can't call methods returning AllocSuccess from within nsTArray (i.e., public methods must be strictly public)? If that's right, perhaps you could write that somewhere. >@@ -93,16 +120,35 @@ struct nsTArrayFallibleAllocator > } > > static void Free(void* ptr) { > moz_free(ptr); > } > > static void SizeTooBig() { > } >+ >+ typedef bool SuccessResult; >+ typedef nsTArrayFallibleSuccessResult SuccessResultProxy; I don't think SuccessResult is a great name, because it kind of sounds like methods which return SuccessResult never fail, but I don't have a better suggestion. I dislike SucceededResult about as much, and it's longer. >@@ -112,16 +158,34 @@ struct nsTArrayInfallibleAllocator >+ static SuccessResultProxy Failure() { >+ return SuccessResultProxy(); >+ } > }; Can we assert (at least at debug time) that this isn't called? I'm tempted to say we should assert in release builds too. >@@ -161,16 +244,34 @@ struct nsTArrayInfallibleAllocator >+ typedef void SuccessResult; >+ typedef nsTArrayInfallibleSuccessResult SuccessResultProxy; >+ >+ static SuccessResult Result(SuccessResultProxy result) { >+ } >+ >+ static bool Successful(SuccessResultProxy) { >+ return true; >+ } >+ >+ static SuccessResultProxy Success() { >+ return SuccessResultProxy(); >+ } >+ >+ static SuccessResultProxy Failure() { >+ return SuccessResultProxy(); >+ } I'm not a huge fan of copy-pasting this (as opposed to, say, using inheritance), but you should do whatever you think is clearer; I don't want to over-complexify the code for the sake of avoiding some copy-paste.
Attachment #727808 - Flags: review?(justin.lebar+bug) → review+
(In reply to Justin Lebar [:jlebar] from comment #9) > Ideally we want the fallible TArray functions to emit a compile warning if > their return results are unused. It's not clear to me how to do so without > abusing macros; do you have an idea for accomplishing that? We don't have to > do it here, of course. Hmm, no I can't think of any elegant solutions. > >diff --git a/xpcom/glue/nsTArray.h b/xpcom/glue/nsTArray.h > >index 1f20c31..527483c 100644 > >--- a/xpcom/glue/nsTArray.h > >+++ b/xpcom/glue/nsTArray.h > > >+// nsTArrayFallibleSuccessResult and nsTArrayInfallibleSuccess results are > >+// proxy types which are used because you cannot use a templated type which > >+// is bound to void as an argument to a void function. In order to work > >+// around that, we encode either a void or a boolean inside these proxy > >+// objects, and pass them to the aforementioned function instead, and then > >+// use the type information to decide what to do in the function. > > So I should return AllocSuccessProxy from internal TArray methods, and I > should > return AllocSuccess from public methods? And I can't call methods returning > AllocSuccess from within nsTArray (i.e., public methods must be strictly > public)? If that's right, perhaps you could write that somewhere. Yes, I'll document that. > >@@ -93,16 +120,35 @@ struct nsTArrayFallibleAllocator > > } > > > > static void Free(void* ptr) { > > moz_free(ptr); > > } > > > > static void SizeTooBig() { > > } > >+ > >+ typedef bool SuccessResult; > >+ typedef nsTArrayFallibleSuccessResult SuccessResultProxy; > > I don't think SuccessResult is a great name, because it kind of sounds like > methods which return SuccessResult never fail, but I don't have a better > suggestion. I dislike SucceededResult about as much, and it's longer. OK I used ResultType and ResultTypeProxy. > >@@ -112,16 +158,34 @@ struct nsTArrayInfallibleAllocator > >+ static SuccessResultProxy Failure() { > >+ return SuccessResultProxy(); > >+ } > > }; > > Can we assert (at least at debug time) that this isn't called? I'm tempted > to > say we should assert in release builds too. OK. > >@@ -161,16 +244,34 @@ struct nsTArrayInfallibleAllocator > >+ typedef void SuccessResult; > >+ typedef nsTArrayInfallibleSuccessResult SuccessResultProxy; > >+ > >+ static SuccessResult Result(SuccessResultProxy result) { > >+ } > >+ > >+ static bool Successful(SuccessResultProxy) { > >+ return true; > >+ } > >+ > >+ static SuccessResultProxy Success() { > >+ return SuccessResultProxy(); > >+ } > >+ > >+ static SuccessResultProxy Failure() { > >+ return SuccessResultProxy(); > >+ } > > I'm not a huge fan of copy-pasting this (as opposed to, say, using > inheritance), but you should do whatever you think is clearer; I don't want > to > over-complexify the code for the sake of avoiding some copy-paste. I'll use inheritance, it should not be too bad.
Attached patch Patch (v2)Splinter Review
Attachment #727808 - Attachment is obsolete: true
Turns out that on Linux, X11/X.h #defines Success, so I will rename Success/Failure to SuccessProxy/FailureProxy to make this compile. :(
Depends on: 856339
Depends on: 856341
Blocks: 856455
Status: NEW → RESOLVED
Closed: 12 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla22
Talking to Benoit about this today, he said that there might have been some simpler C++ tricks that I could have used here. Needinfo'ing him to ask him to take a look at the patch. (Please look at how I have this pattern |return Alloc::SuccessResult();| instead of just |return true;| and also how I have checks such as |if (Alloc::Successful(foo()))| to do a check for the fallible case and do a |if (true)| for the infallible case.
Flags: needinfo?(bjacob)
Indeed, two things: 1. You can use MFBT's mozilla::Conditional to define the return type: typename mozilla::Conditional<IsFallible, bool, void>::Type (as usual, 'typename' is needed to get at member typedefs in types depending on template parameters). 2. You then don't need to wrap the return value in any kind of proxy class, you can simply return true or false cased to the type that you obtained at step 1. Putting this together, here is a toy nsTArray::SetCapacity(): template<typename T, bool IsFallible> typename mozilla::Conditional<IsFallible, bool, void>::Type nsTArray<T, IsFallible>::SetCapacity(size_t newCapacity) { bool success = do_work(newCapacity); return typename mozilla::Conditional<IsFallible, bool, void>::Type(success); } You could evaluate once and for all that awkward Conditional expression in a member typedef in nsTArray.
Flags: needinfo?(bjacob)
Depends on: 969847
You are a true C++ guru! Filed bug 969847 for that.
Blocks: 969864
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: