コード例 #1
0
        private static DynamicMethod EmitDynamicMethod(Type rootType, string path, MethodDescription methodDesc, IEnumerable <Type> realParameterTypes, ICodecContainer codecContainer)
        {
            var dynamicMethod = new DynamicMethod("__dynproxy_" + path + "." + methodDesc,
                                                  methodDesc.ReturnType,
                                                  ConstructorParameterTypes.Concat(realParameterTypes).ToArray(),
                                                  Assembly.GetExecutingAssembly().ManifestModule, true);

            var il = dynamicMethod.GetILGenerator();

            var parameters = methodDesc.Parameters.Select((x, i) => new ParameterNecessity
            {
                Description   = x,
                Codec         = codecContainer.GetEmittingCodecFor(x.Type),
                ArgumentIndex = i + 3
            }).ToArray();

            var requestParameters = parameters
                                    .Where(x => x.Description.Way == MethodParameterWay.Val || x.Description.Way == MethodParameterWay.Ref)
                                    .ToArray();

            var responseParameters = parameters
                                     .Where(x => x.Description.Way == MethodParameterWay.Ref || x.Description.Way == MethodParameterWay.Out)
                                     .ToArray();

            bool hasRetval = methodDesc.ReturnType != typeof(void);
            var  locals    = new LocalVariableCollection(il, responseParameters.Any() || hasRetval);

            var dataArrayVar = il.DeclareLocal(typeof(byte[]));        // byte[] dataArray

            if (requestParameters.Any())
            {
                bool hasSizeOnStack = false;
                foreach (var parameter in requestParameters)
                {
                    switch (parameter.Description.Way)
                    {
                    case MethodParameterWay.Val:
                        parameter.Codec.EmitCalculateSize(             // stack_0 += calculateSize(arg_i+1)
                            il, parameter.ArgumentIndex);
                        break;

                    case MethodParameterWay.Ref:
                        parameter.Codec.EmitCalculateSizeIndirect(     // stack_0 += calculateSizeIndirect(arg_i+1)
                            il, parameter.ArgumentIndex, parameter.Description.Type);
                        break;

                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                    if (hasSizeOnStack)
                    {
                        il.Emit(OpCodes.Add);
                    }
                    else
                    {
                        hasSizeOnStack = true;
                    }
                }

                il.Emit(OpCodes.Newarr, typeof(byte));                // dataArray = new byte[stack_0]
                il.Emit(OpCodes.Stloc, dataArrayVar);
                var dataPointerVar =                                  // var pinned dataPointer = pin(dataArray)
                                     il.Emit_PinArray(typeof(byte), dataArrayVar);
                il.Emit(OpCodes.Ldloc, dataPointerVar);               // data = dataPointer
                il.Emit(OpCodes.Stloc, locals.DataPointer);

                foreach (var parameter in requestParameters)
                {
                    switch (parameter.Description.Way)
                    {
                    case MethodParameterWay.Val:
                        parameter.Codec.EmitEncode(                  // encode(arg_i+1)
                            il, locals, parameter.ArgumentIndex);
                        break;

                    case MethodParameterWay.Ref:
                        parameter.Codec.EmitEncodeIndirect(         // encodeIndirect (arg_i+1)
                            il, locals, parameter.ArgumentIndex, parameter.Description.Type);
                        break;

                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                }

                il.Emit_UnpinArray(dataPointerVar);                 // unpin(dataPointer)
            }
            else
            {
                il.Emit(OpCodes.Ldnull);                            // dataArray = null
                il.Emit(OpCodes.Stloc, dataArrayVar);
            }

            il.Emit(OpCodes.Ldarg_0);                               // stack_0 = methodCallProcessor
            il.Emit(OpCodes.Ldtoken, rootType);                     // stack_1 = typeof(T)
            il.Emit(OpCodes.Call, GetTypeFromHandleMethod);
            il.Emit(OpCodes.Ldstr, string.Format("{0}/{1}",         // stack_2 = SuperServicePath/ServiceName/MethodName
                                                 path, methodDesc.Name));
            il.Emit(OpCodes.Ldarg_1);                               // stack_3 = scope
            il.Emit(OpCodes.Ldloc, dataArrayVar);                   // stack_4 = dataArray
            il.Emit(OpCodes.Ldarg_2);                               // stack_5 = timeoutSettings
            il.Emit(OpCodes.Callvirt, ProcessMethod);               // stack_0 = stack_0.Process(stack_1, stack_2, stack_3, stack_4, stack_5)

            if (responseParameters.Any() || hasRetval)
            {
                il.Emit(OpCodes.Stloc, dataArrayVar);               // dataArray = stack_0
                il.Emit(OpCodes.Ldloc, dataArrayVar);               // remainingBytes = dataArray.Length
                il.Emit(OpCodes.Ldlen);
                il.Emit(OpCodes.Stloc, locals.RemainingBytes);
                var dataPointerVar =                                // var pinned dataPointer = pin(dataArray)
                                     il.Emit_PinArray(typeof(byte), dataArrayVar);
                il.Emit(OpCodes.Ldloc, dataPointerVar);             // data = dataPointer
                il.Emit(OpCodes.Stloc, locals.DataPointer);

                foreach (var parameter in responseParameters)
                {
                    il.Emit(OpCodes.Ldarg, parameter.ArgumentIndex);// arg_i+1 = decode(data, remainingBytes, false)
                    parameter.Codec.EmitDecode(il, locals, false);
                    il.Emit_Stind(parameter.Description.Type);
                }

                if (hasRetval)
                {
                    var retvalCodec = codecContainer.GetEmittingCodecFor(methodDesc.ReturnType);
                    retvalCodec.EmitDecode(il, locals, false);      // stack_0 = decode(data, remainingBytes, false)
                }

                il.Emit_UnpinArray(dataPointerVar);                 // unpin(dataPointer)
            }
            else
            {
                il.Emit(OpCodes.Pop);                               // pop(stack_0)
            }

            il.Emit(OpCodes.Ret);

            return(dynamicMethod);
        }
コード例 #2
0
        public ServiceMethodHandler CreateMethodHandler(ServiceImplementationInfo serviceImplementationInfo, ServicePath servicePath)
        {
            var serviceInterface = serviceImplementationInfo.Description.Type;

            var dynamicMethod = new DynamicMethod(
                "__srpc__handle__" + serviceInterface.FullName + "__" + string.Join("_", servicePath),
                typeof(byte[]), ParameterTypes, Assembly.GetExecutingAssembly().ManifestModule, true);
            var il     = dynamicMethod.GetILGenerator();
            var locals = new LocalVariableCollection(il, true);

            il.Emit(OpCodes.Ldarg_0);                                           // stack_0 = (TServiceImplementation) arg_0
            il.Emit(OpCodes.Castclass, serviceInterface);

            var serviceDesc = serviceImplementationInfo.Description;

            for (int i = 1; i < servicePath.Length - 1; i++)
            {
                var propertyInfo = serviceDesc.Type.GetProperty(servicePath[i]);
                il.Emit(OpCodes.Callvirt, propertyInfo.GetGetMethod());         // stack_0 = stack_0.Property
                SubserviceDescription subserviceDescription;
                if (!serviceDesc.TryGetSubservice(servicePath[i], out subserviceDescription))
                {
                    throw new InvalidPathException();
                }
                serviceDesc = subserviceDescription.Service;
            }

            var methodName = servicePath.MethodName;
            MethodDescription methodDesc;

            if (!serviceDesc.TryGetMethod(methodName, out methodDesc))
            {
                throw new InvalidPathException();
            }

            bool hasRetval  = methodDesc.ReturnType != typeof(void);
            var  parameters = methodDesc.Parameters.Select((x, i) => new ParameterNecessity
            {
                Description   = x,
                Codec         = codecContainer.GetEmittingCodecFor(x.Type),
                LocalVariable = x.Way != MethodParameterWay.Val ? il.DeclareLocal(x.Type) : null,
                ArgumentIndex = i
            })
                              .ToArray();

            var requestParameters = parameters
                                    .Where(x => x.Description.Way == MethodParameterWay.Val || x.Description.Way == MethodParameterWay.Ref)
                                    .ToArray();

            var responseParameters = parameters
                                     .Where(x => x.Description.Way == MethodParameterWay.Ref || x.Description.Way == MethodParameterWay.Out)
                                     .ToArray();

            LocalBuilder requestDataPointerVar = null;

            if (requestParameters.Any())
            {
                il.Emit(OpCodes.Ldarg_1);                                   // remainingBytes = arg_1.Length
                il.Emit(OpCodes.Ldlen);
                il.Emit(OpCodes.Stloc, locals.RemainingBytes);
                requestDataPointerVar =                                     // var pinned dataPointer = pin(arg_1)
                                        il.Emit_PinArray(typeof(byte), 1);
                il.Emit(OpCodes.Ldloc, requestDataPointerVar);              // data = dataPointer
                il.Emit(OpCodes.Stloc, locals.DataPointer);
            }

            foreach (var parameter in parameters)
            {
                switch (parameter.Description.Way)
                {
                case MethodParameterWay.Val:
                    parameter.Codec.EmitDecode(il, locals, false);          // stack_i = decode(data, remainingBytes, false)
                    break;

                case MethodParameterWay.Ref:
                    parameter.Codec.EmitDecode(il, locals, false);          // param_i = decode(data, remainingBytes, false)
                    il.Emit(OpCodes.Stloc, parameter.LocalVariable);
                    il.Emit(OpCodes.Ldloca, parameter.LocalVariable);       // stack_i = *param_i
                    break;

                case MethodParameterWay.Out:
                    il.Emit(OpCodes.Ldloca, parameter.LocalVariable);       // stack_i = *param_i
                    break;

                default:
                    throw new ArgumentOutOfRangeException();
                }
            }

            if (requestParameters.Any())
            {
                il.Emit_UnpinArray(requestDataPointerVar);                  // unpin(dataPointer)
            }

            il.Emit(OpCodes.Callvirt, methodDesc.MethodInfo);                          // stack_0 = stack_0.Method(stack_1, stack_2, ...)

            if (hasRetval || responseParameters.Any())
            {
                IEmittingCodec retvalCodec = null;
                LocalBuilder   retvalVar   = null;

                if (hasRetval)
                {
                    retvalCodec = codecContainer.GetEmittingCodecFor(methodDesc.ReturnType);
                    retvalVar   = il.DeclareLocal(methodDesc.ReturnType);           // var ret = stack_0
                    il.Emit(OpCodes.Stloc, retvalVar);
                    retvalCodec.EmitCalculateSize(il, retvalVar);                   // stack_0 = calculateSize(ret)
                }

                bool hasSizeOnStack = hasRetval;
                foreach (var parameter in responseParameters)
                {
                    parameter.Codec.EmitCalculateSize(il, parameter.LocalVariable); // stack_0 += calculateSize(param_i)
                    if (hasSizeOnStack)
                    {
                        il.Emit(OpCodes.Add);
                    }
                    else
                    {
                        hasSizeOnStack = true;
                    }
                }

                var dataArrayVar = il.DeclareLocal(typeof(byte[]));                 // var dataArray = new byte[size of retval]
                il.Emit(OpCodes.Newarr, typeof(byte));
                il.Emit(OpCodes.Stloc, dataArrayVar);

                var responseDataPointerVar =                                        // var pinned dataPointer = pin(dataArrayVar)
                                             il.Emit_PinArray(typeof(byte), dataArrayVar);
                il.Emit(OpCodes.Ldloc, responseDataPointerVar);                     // data = dataPointer
                il.Emit(OpCodes.Stloc, locals.DataPointer);

                foreach (var parameter in responseParameters)
                {
                    parameter.Codec.EmitEncode(il, locals, parameter.LocalVariable);// encode(data, param_i)
                }
                if (hasRetval)
                {
                    retvalCodec.EmitEncode(il, locals, retvalVar);                  // encode(data, ret)
                }
                il.Emit_UnpinArray(responseDataPointerVar);                         // unpin(dataPointer)
                il.Emit(OpCodes.Ldloc, dataArrayVar);                               // stack_0 = dataArray
            }
            else
            {
                il.Emit(OpCodes.Ldc_I4, 0);                                         // stack_0 = new byte[0]
                il.Emit(OpCodes.Newarr, typeof(byte));
            }

            il.Emit(OpCodes.Ret);
            return((ServiceMethodHandler)dynamicMethod.CreateDelegate(typeof(ServiceMethodHandler)));
        }