About Jolt.NET Libraries

Inspired by the Boost C++ libraries, Jolt.NET aims to complement the .NET Base Class Library (BCL) with algorithms, data structures, and general productivity tools. It is the hope of the authors that the features of Jolt.NET will one day be part of, or represented in the BCL and the .NET Framework.

Abstracting MethodBuilder and ConstructorBuilder

I recently committed a revision to the Jolt.Testing library that changed the internal structure of how methods and constructors are defined. Prior to this change, the ProxyTypeBuilder class was responsible for declaration of all method types, constructor types, their parameters, and their implementations.

When I started to think about how the code should be structured to support generic types and methods, it was soon apparent that the declaration of methods needed to be separated from the definition of the proxy and interface types. Otherwise, I would need to introduce many conditional statements throughout the code to deal with the cases of generic/non-generic subject type and generic/non-generic method. Generic class state would also have to be stored in a shared location as generic type parameters can be used in a method signature. I think I could have written this quickly, but I didn't like what the code was going to look like and proceeded to come up with a structure that looked like the following.



The intent of this class layout is to separate the concerns of declaring methods (AbstractMethodDeclarerImpl) and declaring types of methods (AbstractMethodDeclarer). The AbstractMethodDeclarerImpl.DeclareMethod() method initializes the signature of the method (parameter types and return value), AbstractMethodDeclarerImpl.DefineMethodParameters() initializes the method's parameter names and attributes, and AbstractMethodDeclarer.Declare() is implemented in terms of the latter two methods. Unfortunately, this ideal solution assumes the existence of features that don't exist.

There is no common interface (or base class) between ConstructorBuilder and MethodBuilder.

The above diagram shows a MethodBuilder object in method signatures, however it is the intent is for this object to be an abstraction. This was crucial for both AbstractMethodBuilder[Impl] hierarchies to function as they would need to call the abstract DeclareMethod(), SetParameters(), SetReturnType(), and DefineParameters() methods, in different configurations. While I could create the abstraction manually and delegate to MethodBuilder and ConstructorBuilder as necessary, I won't be able to solve the following problem.

MethodBuilder and ConstructorBuilder have similar but different usage semantics.

If you look at the interface to TypeBuilder, you will notice that you can define a method in one step as part of the TypeBuilder.DefineMethod() method. Similarly, you can do the same for a constructor with TypeBuilder.DefineConstructor(). In addition to TypeBuilder.DefineMethod(), you can define the method in stages by calling MethodBuilder.SetParameters() and MethodBuilder.SetReturnType(). This is useful for introducing generic method parameters, which must be defined in stages via other MethodBuilder methods. Unfortunately, this set of methods are not paralleled to the ConstructorBuilder class. Why? My guess is that it doesn't make sense to do so: there is no such thing as a generic constructor (i.e. class C<T> { C<U>(U u, T t) { /* T is the only valid type /* }), nor can a constructor have a return value. So, you may as well declare the constructor in one shot and avoid creating a ConstructorBuilder with bad state.

Consequently, this second issue breaks the ideal design I was striving for. There is no way I can create an abstraction for MethodBuilder and ConstructorBuilder and cleanly separate the declaration of a method and its parameters -- an absolute requirement when dealing with generic methods. I tried and tried many tricks and hacks to get this to work cleanly, but the above design caveat finally convinced me to throw in the towel. The best I could do was to treat the ConstructorMethodDeclarer as a special case and introduce it into the hierarchy in a less-than-optimal manner. Here is the structure I settled upon.



The handling of generic/non-generic constructors are now done in two distinct types: NonGenericConstructorDeclarer and GenericConstructorDeclarer (TBD). The ConstructorDeclarerImpl class implements the IMethodDeclarerImpl.DefineMethodParameters() method as this call is needed for any method-building type, but throws when IMethodDeclarerImpl.DeclareMethod() is invoked.

To address the common base type issue, I made the base classes generic on two parameters: the method builder type, and the method result type (e.g. MethodBuilder, and MethodInfo). A constraint enforces the semantics that TMethodBuilder derives from TMethod and TMethod derives from MethodBase. This constraint still allows some illegal configurations, but implementing those configuration won't make much sense anyway.

Instantiation of these types is hidden by a factory class that assures objects are connected in the correct manner.

So, what has been created here? If you consider the bridge battern, there really are two bridges collapsed under one interface:

  • InterfaceMethodDeclarer and ProxyMethodDeclarer configured with GenericMethodDeclarerImpl and NonGenericMethodDeclarerImpl
  • NonGenericConstructorDeclarer and GenericConstructorDeclarer configured with ConstructorDeclarerImpl

There are also (or will be) two additional types required (the constructor permutations) when compared with ideal structure. Overall, I believe this solution addresses the issue of separation of concerns (method and type construction) well, given the previously described constraints. If readers have ideas to simplify this solution, please share them in the forum or as comments to this article.

Naturally, the client code in ProxyTypeBuilder has simplified significantly. Here is a snippet showing the usage of this hierarchy.


// Create a constructor on the proxy for each public constructor
// on the real subject type.
foreach (ConstructorInfo constructor in m_realSubjectType.GetConstructors(BindingFlags.Public | BindingFlags.Instance))
{
ILGenerator codeGenerator = m_methodDeclarerFactory.Create(constructor).Declare().GetILGenerator();

// ... implement body ...
}

// Declare the interface and proxy methods.
interfaceMethodBuilder = m_methodDeclarerFactory.Create(MethodDeclarerTypes.Interface, method).Declare();
proxyMethodBuilder = m_methodDeclarerFactory.Create(MethodDeclarerTypes.Proxy, method).Declare();

// ... implement bodies ...

Back from Inactivity

It has been almost one year since I started Jolt.NET and I've finally have been able to put some time aside to resuming work on the first release. I was hoping to have this release completed much sooner, but I've gone through some big life-changes that have kept me away from the code. Given this long period of inactivity, I thought it best to start a blog so readers can keep current on what is being developed, fixed, etc...

The story so far:

I've been working on and off on in the testing library, refactoring the classes that deal with proxy and interface creation. The library must support creation of generic types and methods, and the currently available architecture won't easily support the necessary extension.

The refactoring is complete and the next check-in will enable me to start to work on support for generic types and methods; I'm banking on this refactoring to make the upcoming implementation very straight-forward. Currently, I need to run some ad-hoc tests and make sure that I didn't break anything fundamental in the code-generation process, but the unit tests are giving me high confidence that the program still functions as expected.