I recently had the dubious pleasure of debugging a User 42 Panic on a piece of Symbian code that was given to me by another company. You always need to make sure you understand what the system is telling you, so I went straight to the documentation:
User 42: This panic is raised by a number of RHeap member functions, AllocLen(), Free(), FreeZ(), ReAlloc(), ReAllocL(), Adjust() and AdjustL() when a pointer passed to these functions does not point to a valid cell.
Hmmm. That didn't really give me many clues. Well, I needed to figure out how to reproduce the bug anyway. That was relatively easy, I could reproduce it with a User::Leave(). The leave was being trapped, and the debugger showed the panic was being issued between the Leave and the TRAP, so that pointed to a problem on the CleanupStack (which agreed with what the documentation said). Isolating the problem even further showed that I could reproduce the problem by doing this:
CItemBase* item = (CItemBase*)decoder->ReadItemLC();
CleanupStack::PopAndDestroy(item);
For reference, ReadItemLC() constructed an MDecodable, and the inheritance hierarchy looked like:
Ignoring the fact that a function called ReadItemLC()
should return a CItemBase*
, not an MDecodable*
, the problem here is that in that inheritance hierarchy, both CItemBase
and MDecodable
have their own data (MDecodable
has a vtable pointer, even though it's an abstract class). Consequently when you cast a pointer from an MDecodable*
to a CItemBase*
you get a different pointer value. Try the attached code on any system for a demonstration of the problem.
However, in the code sample above, the CItemBase
class had not been fully defined; we had simply forwarded-declared CItemBase
. That meant the compiler couldn't do the necessary arithmetic (subtract 4) to convert an MDecodable*
to a CItemBase*
- because it didn't know whether they were actually part of the same inheritance tree, or completely dissociated types. So it did the equivalent of a reinterpret_cast
.
When I changed the code to do the correct C++ style cast:
CItemBase* item = static_cast<CItemBase*>(decoder->ReadItemLC());
CleanupStack::PopAndDestroy(item);
It promptly failed to compile, because CItemBase
hadn't been fully defined. Adding
#include "ItemBase.h"
at the top of the file fixed the compile - and fixed my User 42 Panic as well.
Lesson: next time I get code from another source, check the casting is done properly.