Click here to Skip to main content
15,921,351 members
Please Sign up or sign in to vote.
5.00/5 (1 vote)
See more:
I am working on a serializer[^] and it might happen that I have to initialize a readonly ISerializable object.

Currently I successfully do it by calling the serialization constructor on the existing instance.

ex:
C#
SerializationInfo info = ....
var ctx = new StreamingContext(StreamingContextStates.Persistence)
var instance = ...
ConstructorInfo ctor = ... find serialization ctor...
ctor.Invoke(instance, new [] { info, ctx });


Right now I am trying to improve overall performance with System.Emit.
I have some working code (pasted below) that create fast reflection method.
I hacked the code a bit to call constructor on existing instance but I get the an InvalidProgramException("Common Language Runtime detected an invalid program.")

So.. how could I emit this constructor trick?
(unhacked version can be found there[^], for better readability...)

What I have tried:

I have tried to change the emit method as follow (watch for new parameter)

emitted code:
C#
delegate object MethodHandler(object target, params object[] args);

    static readonly ConstructorInfo SecurityPermissionObjectCtor = typeof(System.Security.Permissions.SecurityPermission).GetTypeInfo().GetConstructor(new Type[] { typeof(System.Security.Permissions.SecurityPermissionFlag) });
    static readonly MethodInfo SecurityPermissionDemand = typeof(System.Security.CodeAccessPermission).GetTypeInfo().GetMethod(nameof(System.Security.CodeAccessPermission.Demand));

    public static MethodHandler CreateMethodHandler(MethodBase method, bool ctorMakeNewObj = true)
    {
        var dynam = new DynamicMethod(string.Empty, typeof(object), ManyObjects, Module, true);
        ILGenerator il = dynam.GetILGenerator();

        if (method.IsConstructor && !ctorMakeNewObj)
        {
            il.Emit(OpCodes.Ldc_I4_4);
            il.Emit(OpCodes.Newobj, SecurityPermissionObjectCtor);
            il.Emit(OpCodes.Call, SecurityPermissionDemand);
        }

        ParameterInfo[] args = method.GetParameters();

        Label argsOK = il.DefineLabel();

        il.Emit(OpCodes.Ldarg_1);
        il.Emit(OpCodes.Ldlen);
        il.Emit(OpCodes.Ldc_I4, args.Length);
        il.Emit(OpCodes.Beq, argsOK);

        il.Emit(OpCodes.Newobj, typeof(TargetParameterCountException).GetTypeInfo().GetConstructor(Type.EmptyTypes));
        il.Emit(OpCodes.Throw);

        il.MarkLabel(argsOK);

        il.PushInstance(method.DeclaringType);

        for (int i = 0; i < args.Length; i++)
        {
            il.Emit(OpCodes.Ldarg_1);
            il.Emit(OpCodes.Ldc_I4, i);
            il.Emit(OpCodes.Ldelem_Ref);

            il.UnboxIfNeeded(args[i].ParameterType);
        }

        if (method.IsConstructor)
        {
            if (ctorMakeNewObj)
            {
                il.Emit(OpCodes.Newobj, method as ConstructorInfo);
            }
            else
            {
                il.Emit(OpCodes.Call, method as ConstructorInfo);
            }
        }
        else if (method.IsFinal || !method.IsVirtual)
        {
            il.Emit(OpCodes.Call, method as MethodInfo);
        }
        else
        {
            il.Emit(OpCodes.Callvirt, method as MethodInfo);
        }

        Type returnType = method.IsConstructor ? method.DeclaringType : (method as MethodInfo).ReturnType;
        if (returnType != typeof(void))
            il.BoxIfNeeded(returnType);
        else
            il.Emit(OpCodes.Ldnull);

        il.Emit(OpCodes.Ret);

        return (MethodHandler)dynam.CreateDelegate(typeof(MethodHandler));
    }
Posted
Comments
Mehdi Gholam 16-Jul-16 4:56am    
Oh the pain of IL emit!

Don't bother take a look at fastJSON reflection code :)
Super Lloyd 16-Jul-16 5:11am    
Thanks, doing it... (in GitHub)
I could only find code for parameterless constructor, using NewObj opcode! :((

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900