예제 #1
0
        private static void CreateMethodDelegate(HandlerClassBuildingContext classContext)
        {
            const int implementationArgIndex = 1;
            const int dataArgIndex = 2;
            const int offsetArgIndex = 3;

            var methodBuilder = classContext.Builder.DefineMethod("Handle",
                    MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.HideBySig |
                    MethodAttributes.NewSlot | MethodAttributes.Virtual,
                    typeof(Task<byte[]>), new[] { typeof(object), typeof(byte[]), typeof(int) });

            var il = new MyILGenerator(methodBuilder.GetILGenerator());
            var emittingContext = new HandlerMethodEmittingContext(il, classContext.Fields);

            var serviceDescriptionChain = classContext.ServiceDescriptionChain;

            il.Ldarg(implementationArgIndex);                                       // stack_0 = (TServiceImplementation) arg_1
            il.Castclass(serviceDescriptionChain.First().Type);

            for (int i = 0; i < serviceDescriptionChain.Count - 1; i++)
            {
                var current = serviceDescriptionChain[i];
                var next = serviceDescriptionChain[i + 1];
                il.Callvirt(current.Type.GetProperty(next.Name).GetGetMethod());    // stack_0 = stack_0.Property
            }

            var methodDescription = classContext.MethodDescription;

            var genericArgumentMap = methodDescription.GenericParameters
                .Zip(classContext.GenericTypeParameterBuilders, (p, a) => new KeyValuePair<string, Type>(p.Name, a))
                .ToDictionary(x => x.Key, x => x.Value);

            var parameterDescriptions = methodDescription.Parameters.Select(x => x.DeepSubstituteGenerics(genericArgumentMap)).ToArray();
            var retvalType = methodDescription.ReturnType.DeepSubstituteGenerics(genericArgumentMap);

            var allParameterCodecs = parameterDescriptions.Select((x, i) => new HandlerParameterCodec(emittingContext, x)).ToArray();
            var requestParameterCodecs = allParameterCodecs.Where(x => x.IsRequestParameter).ToArray();
            var responseParameterCodecs = allParameterCodecs.Where(x => x.IsResponseParameter).ToArray();

            if (requestParameterCodecs.Any())
            {
                il.Ldarg(dataArgIndex);                                 // remainingBytes = dataArray.Length - offset
                il.Ldlen();
                il.Ldarg(offsetArgIndex);
                il.Sub();
                il.Stloc(emittingContext.RemainingBytesVar);
                var pinnedVar = il.PinArray(typeof(byte), 2);           // var pinned dataPointer = pin(dataArray)
                il.Ldloc(pinnedVar);                                    // data = dataPointer + offset
                il.Ldarg(offsetArgIndex);
                il.Add();
                il.Stloc(emittingContext.DataPointerVar);
            }

            foreach (var codec in allParameterCodecs)
                codec.EmitDecodeAndPrepare();

            // ReSharper disable CoVariantArrayConversion
            var resolvedMethodInfo = classContext.GenericTypeParameterBuilders.Any()
                ? methodDescription.MethodInfo.MakeGenericMethod(classContext.GenericTypeParameterBuilders)
                : methodDescription.MethodInfo;
            il.Callvirt(resolvedMethodInfo);                            // stack_0 = stack_0.Method(stack_1, stack_2, ...)
            // ReSharper restore CoVariantArrayConversion

            switch (methodDescription.RemotingType)
            {
                case MethodRemotingType.Direct:
                    EmitProcessAndEncodeDirect(emittingContext, responseParameterCodecs, retvalType);
                    break;
                case MethodRemotingType.AsyncVoid:
                    EmitProcessAndEncodeAsyncVoid(emittingContext);
                    break;
                case MethodRemotingType.AsyncWithRetval:
                    EmitProcessAndEncodeAsyncWithRetval(classContext, emittingContext, retvalType.GetGenericArguments()[0]);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            il.Ret();
        }