Ayende @ Rahien

Hi!
My name is Oren Eini
Founder of Hibernating Rhinos LTD and RavenDB.
You can reach me by phone or email:

ayende@ayende.com

+972 52-548-6969

, @ Q c

Posts: 5,953 | Comments: 44,410

filter by tags archive

Can you hack this out? Revised


It appears that I made this challenge a bit too hard, I wrote it in haste and didn’t check that it all works. Here is the new code, it is almost the same as the first, but now it doesn’t check the typeof(T) (for which there is no interception path), but the provided runnerType instead.

internal interface IRunner
{
void Execute();
}

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

public class CompositeRunner
{
private readonly Type runnerType;

public CompositeRunner(Type runnerType)
{
this.runnerType = runnerType;
}

public void Execute()
{
if (!typeof(IRunner).IsAssignableFrom(runnerType))
throw new InvalidOperationException("invalid type");
var runner = (IRunner)Activator.CreateInstance(runnerType);
Console.WriteLine("starting");
runner.Execute();
Console.WriteLine("done");
}
}

Sorry about that image

Look at the future posts for hints for that.


Comments

Omer Mor

Now THAT'S easy!

I have a great solution for this, but it doesn't fit in this comment. :-)

Alex Simkin

OK. You already forced me to look at ProfilingSqlProviderProxyType, so I cannot participate.

Pierre Fermat

Omar,

Been There, Done That!

Pierre

Stefan Wenig

now it seems almost simple. you can create a RealProxy for IRunner

class MyProxy : RealProxy {

MyProxy() : base (Type.GetType ("IRunner...")

and put a custom ProxyAttribute on the type you pass (must be derived from ContextBoundObject). Some fumbling with Invoke(), that should basically be it...

Ayende Rahien

Stefan,

Write the code, there is still one trip wire there.

Fabian Schmied

The one trip still there is the " if (!typeof(IRunner).IsAssignableFrom(runnerType))" check.

And this can be solved by:

  • subclassing System.Type (in actual C# code, no hackery required) and

  • overriding UnderlyingType

    • to return typeof (IRunner) on the first call and

    • typeof (MyContextBoundObject) on the second call.

Strangely, this works, it really does. At the moment, at least. Aren't you afraid to actually sell a piece of software based on this hack? :)

Stefan Wenig
Oh right, I was so p***ed about the cast in CreateInstance
<t that I forgot about that one. Couldn't find a better solution than Fabian either, everything else seems to end up in something internal/sealed. (I even had to look at the SSCLR, because reflector screws up in Type.IsAssignableFrom... enough unnatural acts for this month!)
  
So is this it, or is there a better way?
>
Ayende Rahien

Fabian,

I am really scared by that, yes.

OTOH, what other choice do I have?

Stefan Wenig

CreateInstance(Of T) that is. No angle brackets, must be a VB lover's blog ;-)

BTW, just when I realized that R# can't go all the way when subclassing Type via delegation to a member, I discoverd System.Reflection.TypeDelegator. Nice!

Insanely, I agree with you Ayende. If this changes in vNext, you'll just have to come up with another unnatural hack. Still better than Profiling APIs by any means.

Frank Quednau

Yes, this looks more doable.

@Omer nice Fermat analogy there :)

This hack just goes to show that there is no true privacy in such a system, simplest example being how easy it is to instantiate a type through a private constructor. I wonder how the CLR would want to avoid such things from happening. After all it needs to interrogate the type to answer whether it is assignable and Type is an abstract class in which you can override stuff.

Jay R. Wren

I CAN'T TAKE IT ANYMORE! I've spent too many hours on this already... well, ok... only 2.5, but that is too many for me.

Once Fabian suggested deriving from Type, I got past the IsAssignableFrom... And I'm using ContextBoundObject to return a RealProxy from Activator.CreateInstance, but I cannot get past the unable to cast transparent proxy to type IRunner

What is the solution?

Fabian Schmied

Jay, implement IRemotingTypeInfo on your real proxy, then the cast isn't an issue any longer.

Jay R. Wren

Fabian,

Thanks. I had that, but removed it when I went with a different approach.

... I'm learning WAY too much here :)

Omer Mor

I tried to implement the solution according to the comments, but I still fail to get past the "unable to cast transparent proxy" part.

Can you give me a hint?

This is my solution so far:

public class Program

{

private static void Main()

{

    var myType = new MyType

<assemblyrunner();

    var compositeRunner = new CompositeRunner(myType);

    compositeRunner.Execute();

}

}

public class MyProxyAttribute : ProxyAttribute

{

public override RealProxy CreateProxy(ObjRef objRef1, Type serverType, object serverObject,

                                      Context serverContext)

{

    Type classToProxy = typeof (AssemblyRunner).GetInterface("ThirdParty.IRunner");

    return new MyProxy(classToProxy);

}

}

[MyProxy]

public class MySubclass : ContextBoundObject

{

}

public class MyType{T} : TypeDelegator

{

private bool m_firstTime = true;


public MyType() : base(typeof (T))

{

}


public override Type UnderlyingSystemType

{

    get

    {

        if (m_firstTime)

        {

            m_firstTime = false;

            return typeof (T);

        }

        return typeof (MySubclass);

    }

}

}

public class MyProxy : RealProxy, IRemotingTypeInfo

{

public MyProxy(Type classToProxy) : base(classToProxy)

{

}


public bool CanCastTo(Type fromType, object o)

{

    return true;

}


public string TypeName { get; set; }


public override IMessage Invoke(IMessage msg)

{

    return msg;

}

}

Van
Van

Done it, so as hard as I thought.

This challenge suddenly much easier without the generic type.

Basically, I use:

  • TypeDelegator to pass IsAssignableFrom

  • ContextBoundObject, ProxyAttribute and RealProxy to create an interceptor for the instance.

  • IRemotingTypeInfo to pass the type casting check.

All method call are then redirected to the real runner.

I don't see any chance that there will be any change that will cause this code not to run in next versions of .NET Framework.

TypeDelegator is used for the purpose it was created, that is delegating the type information.

Proxy is used to redirect messages, which is also what it was created for.

The only place that I used a real hack is in the GetInterfaces of TypeDelegator when I use StackFrame to see who called the method to return the appropriate result.

Alkampfer

So there is no solution for the original one? I was really curious on how you hacked the typeof(T) :P

Alk.

Comment preview

Comments have been closed on this topic.

FUTURE POSTS

No future posts left, oh my!

RECENT SERIES

  1. The RavenDB Comic Strip (3):
    28 May 2015 - Part III – High availability & sleeping soundly
  2. Special Offer (2):
    27 May 2015 - 29% discount for all our products
  3. RavenDB Sharding (3):
    22 May 2015 - Adding a new shard to an existing cluster, splitting the shard
  4. Challenge (45):
    28 Apr 2015 - What is the meaning of this change?
  5. Interview question (2):
    30 Mar 2015 - fix the index
View all series

Syndication

Main feed Feed Stats
Comments feed   Comments Feed Stats