Parsing “Unsafe” Method Signatures

If you’ve ever used JustMock then you should be familiar with code fragments like this one:

public interface IFoo
{
    byte Bar(int i, string s, long l);
}

var foo = Mock.Create<IFoo>();
Mock.Arrange(() => foo.Bar(123, "test", 321)).Returns(5);

The interesting thing in this code is in the Arrange method. Its signature is as follows:

public static FuncExpectation<TResult> Arrange<TResult>(Expression<Func<TResult>> expression)

The method accepts a single parameter of Expression<TDelegate> type. This allows JustMock to parse the actual method name and the parameter values. In this blog post I am going to show you a simple and yet effective approach for parsing methods that contain pointer parameters.

Lets make our example more interesting.

public unsafe interface IFoo
{
    byte** Bar(int* i, string s, ref long* l);
}

Because Expression<TDelegate> expects a delegate type as a type parameter we can define a new delegate type and use it to construct a proper expression object.

public unsafe delegate byte** BarDel();
//..
int* pi = (int*)IntPtr.Zero;
long* pl = (long*)IntPtr.Zero;
Expression<BarDel> expr = () => Bar(pi, "test", ref pl);

This is all good and nice. Now we can change Arrange method signature as follows:

// to keep it simple, we changed the return type to void
public static void Arrange<TDelegate>(Expression<TDelegate> expr) { }
//..
int* pi = (int*)IntPtr.Zero;
long* pl = (long*)IntPtr.Zero;
Mock.Arrange<BarDel>(() => Bar(pi, "test", ref pl));

The only thing that left is to make the return type independent from the return type of the actual Bar method. This is easy. A pointer to a pointer to a byte (byte**) is just… a pointer! The only tricky thing is that JustMock doesn’t know what actual type is pointed. So, it is a sane decision to leave this knowledge to the unit test author. We need a simple generalization over IntPtr type and that’s all.

namespace Telerik.JustMock
{
    public class PtrBase
    {
        private readonly IntPtr addr;

        protected PtrBase(IntPtr addr)
        {
            this.addr = addr;
        }

        public IntPtr Addr { get { return this.addr; } }
    }

    public delegate T PtrDel<T>() where T : PtrBase;
}

Now, we have to change Arrange method as follows:

public static void Arrange<TPtr>(Expression<PtrDel<TPtr>> expr, params TPtr[] arr)
    where TPtr : PtrBase
{
    var methodCallExpr = (expr.Body as UnaryExpression).Operand as MethodCallExpression;

    // process methodCallExpr
}

Lets see how we have to change our test code so we can call the new Arrange method. Now, we have to take care for all pointers and convert them. This is a trivial task:

public class Ptr : PtrBase
{
    private Ptr(IntPtr addr) : base(addr) { }

    public unsafe static implicit operator Ptr(int* ptr)
    {
        return new Ptr(new IntPtr(ptr));
    }

    public unsafe static implicit operator Ptr(long* ptr)
    {
        return new Ptr(new IntPtr(ptr));
    }

    public unsafe static implicit operator Ptr(byte** ptr)
    {
        return new Ptr(new IntPtr(ptr));
    }
}

Finally, we have to change our Arrange method invocation:

int* pi = (int*)IntPtr.Zero;
long* pl = (long*)IntPtr.Zero;
Mock.Arrange<Ptr>(() => Foo(pi, "test", ref pl), pi, pl);

The code can be improved in many ways. One way to improve it is to replace PtrDel<T> with Func<TResult> though I prefer the explicit constraint on PtrBase. Another way to improved the code is by making PtrBase abstract type and providing a template method that will be called inside the Arrange method, so the actual value can be retrieved. Here is a simple implementation:

namespace Telerik.JustMock
{
    public abstract class PtrBase
    {
        private readonly IntPtr addr;
        private readonly int cookie;

        protected PtrBase(IntPtr addr, int cookie)
        {
            this.addr = addr;
            this.cookie = cookie;
        }

        public IntPtr Addr { get { return this.addr; } }
        public int Cookie { get { return this.cookie; } }

        public abstract object ReadValue();
    }
}

When you create a new type which inherits from PtrBase type you should provide a unique cookie for each pointer type (e.g. 1 for int*, 2 for long*, 3 for byte**, etc.). When Arrange method is called, it will call in turn your implementation of ReadValue method and use the return value. In a matter of fact we don’t need this cookie mechanism because we can get the actual pointer type from methodCallExpr variable. The real purpose of ReadValue is to allow execution of user defined code in a lazy fashion. In case you don’t want this feature you can read the actual pointed value inside Ptr implicit conversion operator implementation and pass it to PtrBase constructor.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.