A Bug Story

time to read 4 min | 693 words

I think that it may be interesting to show how I diagnosed and fixed a bug in Rhino Mocks. Here is the story, only slightly embellished

I got an email from Royston Shufflebotham, who has recently been putting Rhino Mocks through its paces, with a failing test, saying that under a certain condition, an exception was thrown from Rhino Mocks. I can’t overestimate the value that a failing test has when coming to fix a bug.

It lets you repeatedly reproduce the problem, which directs you to the problem, and it makes sure that you understand what the expected behavior is.

In this case, the test was trying to create a mock object from multiply types (one of them a class, and the other interfaces), a feature recently added to Rhino Mocks. The problem occurred when the base class implemented an interface non virtually, and one of the interfaces that was passed to Rhino Mocks also inherited from that interface.

I know this is unclear, but it is better with code:

public interface IMulti

{

void OriginalMethod1();

void OriginalMethod2();

}

public class MultiClass : IMulti

{

// NON-virtual method

public void OriginalMethod1() { }

// VIRTUAL method

public virtual void OriginalMethod2() { }

}

public interface ISpecialMulti : IMulti

{

void ExtraMethod();

}

 

As you can see, MutliClass implements IMulti non-virually, while ISpecialMuli inherits from IMulti. The problem occured when you attempt to create a mock object from MutliClass and ISepscialMulti , like this:

mocks.CreateMultiMock(typeof(MultiClass), typeof(ISpecialMulti))

The exception that was thrown was: “TypeLoadException: Declaration referenced in a method implementation cannot be a final method”, which is not something that I have seen before.

This is a good point to thanks the gods of software for exceptions. I would hate to get an error code like 0xFEABAADC and try to figure out what it means. Exceptions are also googleble, which was exactly what I did.

It lead me to this forum question, which indicated that the problem was trying to override a non virtual method. Apparently the VB compiler sometimes do not make this checks, and it results in a runtime error. The C# compiler does make this check, which is probably why I have never seen this error before.

Once I knew what the problem was, it was a matter of finding out how Dynamic Proxy handles generating methods. After some investigation, I found that Dynamic Proxy always generate virtual methods. In this case, of course, it caused a type load exception.

A brief overview of the way the CLR implements method tables is in order, I think. If you come from a C++ background, you are familiar with the notion of method tables. In the CLR, there is a similar notion, but here it also has the idea of a non virtual override (new in C#, shadows in VB). This means that a new slot in the method table is created, even though the method signature matches the signature of a method in a base type.

I knew that I had a problem here because I was trying to override a non virtual method, this meant that I needed to create a new slot in the method table when I was generating the type. I won’t get into the technical details (basically, changing the MethodAttributes when generating the call), but it is not complex.

The idea is to simple track all final methods per type, and generate a new slot when I needed to generate a method with the same signature. It sounds simple, and it is simple.

I had a fix in half an hour or so from the moment I realized what the problem is. Then all I had to do is to update the version number, run the build scripts a couple of time, and push the new release to the server.