Ayende @ Rahien

Refunds available at head office

Can you hack this out?

I recently had to take a really deep look into how to cheat the CLR, that brought about some interesting discoveries, including the fact that it is, surprisingly, possible to do so.

Let us say that you have this code in some 3rd party assembly that you cannot modify:

internal interface IRunner
{
void Execute();
}

public sealed class AssemblyRunner : IRunner
{
void IRunner.Execute()
{
Console.WriteLine("executing");
}
}

public class CompositeRunner<T> where T : new()
{
public void Execute()
{
if(!typeof(IRunner).IsAssignableFrom(typeof(T)))
throw new InvalidOperationException("invalid type");
var runner = (IRunner)new T();
Console.WriteLine("starting");
runner.Execute();
Console.WriteLine("done");
}
}

Can you get the CompositeRunner to output:

    • starting
    • before executing
    • executing
    • after executing
    • done

The real problem was a bit harder, but this is a good start.

Comments

eti
11/19/2009 10:24 AM by
eti

Hi,

I think that

if(typeof(IRunner).IsAssignableFrom(typeof(T)))

should be

if( ! typeof(IRunner).IsAssignableFrom(typeof(T)))

Pierre Murasso
11/19/2009 10:25 AM by
Pierre Murasso

Using Aspect Oriented Programming?

For instance you can write Aspects that get injected into this assembly during PostCompilation, although this is not CLR hacking.

Ayende Rahien
11/19/2009 10:32 AM by
Ayende Rahien

Eti,

Yes, it should, fixed, thanks :-)

Pierre,

You can't modify the original assembly in any way.

Glenn F. Henriksen
11/19/2009 10:33 AM by
Glenn F. Henriksen

I have no idea on how you did it. You, Sir, are a machine!

Is this something that could be used for mocking unmockable classes? (Sealed, non-virtual, etc)

eti
11/19/2009 10:38 AM by
eti

Hi again,

Well i'm sure you are thinking about something else but this code outputs the expected values:

class Test : IRunner {

        public void Execute()     {

            Console.WriteLine("before executing");

            Console.WriteLine("executing");

            Console.WriteLine("after executing");

        }

    }

CompositeRunner <test c = new CompositeRunner <test();

c.Execute();

You probably are asking about CompositeRunner <assemblyrunner.

configurator
11/19/2009 10:38 AM by
configurator

Use reflection to find the interface that AssemblyRunner implements, and implement it yourself in your own emitted class?

Van
11/19/2009 10:39 AM by
Van

public class MyRunner : IRunner

{

private readonly AssemblyRunner _inner = new AssemblyRunner();

public void Execute()

{

Console.WriteLine("before executing");

_inner.Execute();

Console.WriteLine("after executing");

}

}

new CompositeRunner <myrunner().Execute();

ralf
11/19/2009 10:42 AM by
ralf

How about that?

    private class MyRunner : IRunner

    {

        private readonly IRunner inner = new AssemblyRunner();



        public void Execute()

        {

            Console.WriteLine("before executing");

            this.inner.Execute();

            Console.WriteLine("after executing");

        }

    }



    [TestMethod]

    public void TestMethodName()

    {

        var runner = new CompositeRunner

<myrunner();

        runner.Execute();

    }
Van
11/19/2009 10:44 AM by
Van

Sorry, did not notice IRunner is internal.

Igal Tabachnik
11/19/2009 10:45 AM by
Igal Tabachnik

You could probably 'TypeMock' it, and use Profiler API to hook the method...?

David Hanson
11/19/2009 10:48 AM by
David Hanson

Ralf wont your call to inner.Execute() be in accessible as its private?

Well this could be done quite easily with dynamic dispatch in .net 4.0. Something tells me we cant use that however. :-)

Ayende Rahien
11/19/2009 10:59 AM by
Ayende Rahien

Eti,

IRunner is an internal interface in another assembly, you cannot inherit from it.

Ayende Rahien
11/19/2009 10:59 AM by
Ayende Rahien

Configurator,

Try it :-)

I can tell you it... isn't easy

Ayende Rahien
11/19/2009 11:00 AM by
Ayende Rahien

Van & Ralf,

IRunner is an internal interface in another assembly, your code won't compile

Ayende Rahien
11/19/2009 11:01 AM by
Ayende Rahien

Igal,

Show me working code to make this work.

The profiling API are... possible, but they are a bigger cheat than I would consider.

Ayende Rahien
11/19/2009 11:02 AM by
Ayende Rahien

David,

Try solving this using 4.0, it would be interesting to see where it trips you

Chris Smith
11/19/2009 11:22 AM by
Chris Smith

Ignore the rules specified. Use Mono.Cecil. Rewrite the assembly.

Stefan Wenig
11/19/2009 11:24 AM by
Stefan Wenig

OK, time for a hint. Did you find a way to implement an internal interface using reflection.emit? To derive from a sealed class? Make the CLR think your type implemented an interface while it did not ... quite? Something else altogether?

configurator
11/19/2009 11:28 AM by
configurator

Ah, yes, I see the problem.

Type 'Implementation' from assembly 'NewAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' is attempting to implement an inaccessible interface.

Stefan Wenig
11/19/2009 11:37 AM by
Stefan Wenig

... just when I was going to say: if the solution involves in-proc remoting, then I don't want to know it ;-) bah!

configurator
11/19/2009 11:41 AM by
configurator

The problem with using C# 4's dynamic is that you can't cast it to the interface (I think)

Ayende Rahien
11/19/2009 11:42 AM by
Ayende Rahien

Chris,

You can't, assume this is a framework assembly

Stefan,

I will give you a hint, remoting...

Hristo,

That is a good first step, but it won't solve this particular problem.

I am not asking you to replace a live instance, I am asking you to change the way it works when I create a new instance.

Dave Hanson
11/19/2009 11:48 AM by
Dave Hanson

Is transparent proxy near the mark? :-)

Ayende Rahien
11/19/2009 11:59 AM by
Ayende Rahien

Dave,

Yes & no.

Try to get a working sample, you'll see what I mean.

Krzysztof Kozmic
11/19/2009 12:50 PM by
Krzysztof Kozmic

I haven't tried solving this puzzle yet, but it seems more complicated than I thought.

Closed generic type complicates things significantly...

Ayende Rahien
11/19/2009 12:55 PM by
Ayende Rahien

Krzysztof,

That is more or less the point :-)

liviu
11/19/2009 01:11 PM by
liviu

Ayende, Are you looking for a hack or do you really have one, because

the code you posted does not compile:

public sealed class AssemblyRunner : IRunner

{

private void IRunner.Execute()

{

    Console.WriteLine("executing");

}

}

Compile Error 1 The modifier 'private' is not valid for this item

liviu
11/19/2009 01:30 PM by
liviu

The core of the problem is the line:

if(!typeof(IRunner).IsAssignableFrom(typeof(T)))

the call to IsAssignableFrom

Looking into Type.IsAssignableFrom code, it maybe a path to implement a TypeBuilder ...

Ayende Rahien
11/19/2009 01:38 PM by
Ayende Rahien

liviu,

I have a hack, and thanks for catching that issue, fixed that.

And a type builder wouldn't quite work here.

Ayende Rahien
11/19/2009 03:06 PM by
Ayende Rahien

Dave,

You can't modify the assembly, and try it with proxies, see where it gets you

Alkampfer
11/19/2009 03:16 PM by
Alkampfer

Is something related to ContextBoundObject?

Alk.

Pixel
11/19/2009 03:26 PM by
Pixel

Why not:

public class MyCompositeRunner <t : CompositeRunner <t
{

public new void Execute()

{

// bla bla

}

}

?

Ayende Rahien
11/19/2009 03:31 PM by
Ayende Rahien

Alkampfer,

That is related, in some way, yes

Ayende Rahien
11/19/2009 03:32 PM by
Ayende Rahien

Pixel,

That won't work, when you pass CompositeRunner to something that will call its Execute method, your method won't get called.

Marc-Andr&#233; Bertrand
11/19/2009 03:34 PM by
Marc-André Bertrand

public class WrapperRunner <t : IRunner where T : IRunner, new()

{

public void Execute()

{

    Console.WriteLine("Before running");

    var runner = new T();

    runner.Execute();

    Console.WriteLine("After running");

}

}

var composite = new CompositeRunner <wrapperrunner();

its a bit weird but should work

composite.Execute();

Ayende Rahien
11/19/2009 03:36 PM by
Ayende Rahien

Marc,

IRunner is internal, you can't inherit it.

Marc-Andr&#233; Bertrand
11/19/2009 03:40 PM by
Marc-André Bertrand

You should escape the brackets. It prevents us from posting code with generics and it could allow cross-site scripting.

Al
11/19/2009 04:13 PM by
Al

The solution must use ContextBoundObjects, message sinks, and transparent proxies. Although, it's been a while since I've dug into those internals, I think you just need to find a way to get the CompositeRunner participating in the sink chain?

I can't picture the code, but it doesn't feel impossible. I don't have time to experiment right now, but it's a nice puzzle. Looking forward to your solution.

Alkampfer
11/19/2009 04:17 PM by
Alkampfer

Actually I have no time to try a solution, but the right direction can be trying to load the sealed object in another AppDomain, since communication between AppDomains always happens with a proxy. When you create object in another Appdomain all communication happens with a remoting proxy.

The only problem is that the creation of the sealed object happens inside the CompositeRunner, that directly called

var runner = (IRunner)new T();

You probably found a way to make this code creates T object in another appDomain?

Alk.

fred
11/19/2009 04:45 PM by
fred

I should not call the execute method directly, but have to wrap it in a proxy, or put the method in a delegate and pass the delegate into a execute method, in the execute method to add your decoration before and after running the delegate

Kyle Szklenski
11/19/2009 05:09 PM by
Kyle Szklenski

It seems like there should be a way of doing this by deriving from Type. I'm just not sure offhand how to implement it in such a way that it would do that. Reflecting on IsAssignableFrom doesn't really seem to help as much as I'd hoped it would (or I'm too dense to see the right way to work it based on that).

Bunter
11/19/2009 07:18 PM by
Bunter

No, I can't hack this out. Enlighten us, Ayende.

Paulo K&#246;ch
11/20/2009 12:39 AM by
Paulo Köch

Something around latent typing? Can't recall I f I'll need to reference the internal interface, though.

Maybe getting down to pointer/memory level?

If all else fails, remoting, as Ayende said. I remember seeing some ORM using that technique.

Ayende Rahien
11/20/2009 02:12 AM by
Ayende Rahien

Al,

Try it :-)

Alkampfer,

No second app domain is required.

And yes, there is a trick here.

Fred,

Show me the code

Kyle,

You are in the right direction, now take it to the illogical conclusion.

Paulo,

Latent typing? I can't see a solution to that here, but maybe you can educate me.

I thought about doing pointers here, but it is not needed.

Remoting would work,but how?

Ayende Rahien
11/20/2009 02:13 AM by
Ayende Rahien

Al,

You are close, but not quite.

Dennis
11/20/2009 07:18 AM by
Dennis

I saw an article the other day about hooking into the JIT and hijacking the jump table. But I cannot find the reference again :(

SeeR
11/20/2009 08:39 AM by
SeeR

I remember your tweets about AppDomain.AssemblyResolve and hacking LINQ2SQL IProvider.

The best was "how to override System.RuntimeType at runtime using Reflection.Emit "

I guess this is it :-)

SeeR
11/20/2009 10:05 AM by
SeeR

Hmm. I'm guessing that now, you know also the way to mock static methoda with Rhino Mocks :-)

Alkampfer
11/20/2009 11:08 AM by
Alkampfer

If you do not use another appDomain, probably a possibility is to inherit from Type, override the ISAssignableFrom(Type) method and return true, this fools the Runner and makes the following test pass

if(!typeof(IRunner).IsAssignableFrom(typeof(T)))

        throw new InvalidOperationException("invalid type");

then maybe it is possible with reflection.emit, to create a new class, and use your custom Type as type, so when the runner call typeof(T) it gets your MyType that return true when it ask for IsAssignableFrom, and it gets fooled.

But actually you still have another problem, because the IRunner is internal interface, and even with reflection Emit you cannot create a class that implements it, so you can make this class inherits from ContextBoundObject and use the standard IMessage AOP technique to intercept calls and redirect to a real AsseemblyRunner object, and now you can inject code before and after the call :)

I wish to have some time to check this path and verify where it leads :)

Alk.

Whut
11/20/2009 03:32 PM by
Whut

I haven't done any remoting or COM, so I'm just fooling around.

I'm trying to use proxy of Type class and using it as generic argument to CompositeRunner, but I can't just cast transparent proxy to Type;)

Alex Simkin
11/20/2009 03:43 PM by
Alex Simkin

@Whut Transparent proxy is an object not a type. If you want its type, use GetType(), but I do not see what it gives you.

anon
11/21/2009 06:27 AM by
anon

No clue how :)

But, can this somehow be used to provide a undo/redo facility that is transparent to the objects? Somehow catching the get/set of a property and also enhancing any property in a object with Undo() and Redo() methods?

Mags
11/21/2009 07:21 AM by
Mags

Care to post the solution?

Gian Maria
11/21/2009 07:57 AM by
Gian Maria

I spent this morning 30 minutes thinking to a solution, it is really a thought question :).

Ayende, it is somewhat related to creating a dynamic class with reflection.Emit and changing manually the TypeToken, setting with reflection the m_tdType field of the TypeBuilder? when the compositeRunner checks if T implements IRunner uses typeof(T) that in MSIL generates a call to GetTypeFromHandle using the TypeToken of T...

Alk

Whut
11/21/2009 12:54 PM by
Whut

Using remoting, message sinks and some reflection hacks I'm able to run those lines:

var runner = (IRunner)new T();

runner.Execute();

Still I can't crack this

if(!typeof(IRunner).IsAssignableFrom(typeof(T)))

But I will find it;)

olcay şeker
11/22/2009 01:44 PM by
olcay şeker

@ayende

will you unveil the answer???

Alex Simkin
11/22/2009 10:15 PM by
Alex Simkin

@olcay şeker

Apparently we didn't ask kindly enough.

Anthony Dewhirst
11/22/2009 10:27 PM by
Anthony Dewhirst

Please Don't give the answer, maybe some hints so we can try to figure it out. (I might be the only fool asking for this though)

liviu
11/22/2009 11:14 PM by
liviu

I think the solution can be found by using resharper on L2S Profiler :--))

David
11/22/2009 11:53 PM by
David

I agree with Anthony, can we have some more hints, at work we are having fun in our lunch break on this one ;)

@ Whut, how does you code look right now?

I think to get round the IsAssignableFrom, you have to Inherit off the Type Class, override this function to return true and then use the System.Reflection.Emit to force the code to use your Type class. thing is I have yet to figure out how to do that

@ Ayende, the class type you pass into the CompositeRunner does not inherit of IRunner? its just a class that has an execute function, and you make it always return true on the IsAssignableFrom?

Alex Simkin
11/23/2009 01:39 AM by
Alex Simkin

@liviu

"resharper on L2S Profiler :--))"

Did that, understand how he solved the task at hand, still cannot solve this one.

Anthony Dewhirst
11/23/2009 06:56 AM by
Anthony Dewhirst

David:

if you do override IsAssignableFrom you still have to get past the cast on the next line.

Ayende:

Do you need to some how push the IRunner interface that you get using reflection into the RuntimeTypeCache for the new object or am I heading in the wrong direction?

Van
11/23/2009 07:45 AM by
Van

Anthony Dewhirst:

I think the cast can be handled with explicit operator.

I still cannot figure out how to trick the typeof(T) to return the Type that I wanted.

hpower11
11/23/2009 11:05 AM by
hpower11

Ayende:

You said "The real problem was a bit harder" And by far no one has manage to solve the "easier one"

Can you please enlighten us with at list some hints. Also I am very curious about the real challenge you had.

liviu
11/23/2009 11:28 AM by
liviu

I said resharper, i ment reflector...

I think the challange is related either to EF or to L2S profiling...

Ayende Rahien
11/24/2009 11:26 AM by
Ayende Rahien

Arielr,

That is pretty much cheating.

dexon
11/25/2009 08:15 PM by
dexon

Maybe something like this:

var irunner = typeof(AssemblyRunner).GetInterface("IRunner");

....

TypeBuilder myType = myModule.DefineType("MyRunner",

            TypeAttributes.Public | TypeAttributes.Class, null, new Type[]{ irunner });

MethodBuilder exMethod =

            myType.DefineMethod("Execute", MethodAttributes.Public, typeof(void), new Type[]{});

ILGenerator ilgen = exMethod.GetILGenerator();

ilgen.EmitWriteLine("before executing");

// call to AssemblyRunner.Execute

ilgen.EmitWriteLine("after executing");

Type finished = myType.CreateType();

Type[] typeArgs = {finished};

Type constructed = typeof(CompositeRunner<>).MakeGenericType(typeArgs);

MethodInfo mi = constructed.GetMethod("Execute");

mi.Invoke(Activator.CreateInstance(constructed, null, null), null);

It's allready passing through

typeof(IRunner).IsAssignableFrom(typeof(T))

but failing on

var runner = (IRunner)new T();

Arielr
11/26/2009 07:06 PM by
Arielr

Oren,

Yeah, I know.

Fun, ain't it? :)

Mark Whitfeld
11/27/2009 07:39 AM by
Mark Whitfeld

Hmmm.... If this is possible, then I'm hoping that there are some pretty ground breaking improvements to Rhino Mocks in the near future :)

Comments have been closed on this topic.