private void InitMethodsFromType(Type type, bool isAttributedMethodsOnly) { foreach (var methodInfo in type.GetMethods()) { RpcMethodAttribute methodAttr = GetMethodAttribute(methodInfo); if (methodAttr != null || !isAttributedMethodsOnly) { RpcMethodDescriptor method = new RpcMethodDescriptor(methodInfo, methodAttr, name); if (methods.ContainsKey(method.Name)) { methods[method.Name].Merge(method); } else { methods.Add(method.Name, method); } } } //for classes checking the public methods is enough, but for an interface we must check the other //interfaces implemented too if (type.IsInterface) { foreach (var interfaceType in type.GetInterfaces()) { InitMethodsFromType(interfaceType, isAttributedMethodsOnly); } } }
public void InvokeMethod(object service, string methodName, List <RpcMessage.Parameter> parameters, RpcMessage resultMessage) { RpcMethodDescriptor method = GetMethod(methodName); if (method != null) { method.Invoke(service, parameters, resultMessage); } else { //return an error message to the client if it is expecting a result, otherwise we fail silently if (resultMessage != null) { resultMessage.ResultMessage.IsFailed = true; resultMessage.ResultMessage.ErrorMessage = String.Format("Unknown method '{1}' in service '{0}'", Name, methodName); } } }
public void Merge(RpcMethodDescriptor method) { Debug.Assert(method.name == name && method.serviceName == serviceName); if (returnType != null && method.returnType != null && returnType != method.returnType) { throw new ArgumentException( String.Format("Invalid declarations for method '{0}', the return types are not consistent.", name)); } if (parameterTypes != null && method.parameterTypes != null && !parameterTypes.SequenceEqual(method.parameterTypes)) { throw new ArgumentException( String.Format("Invalid declarations for method '{0}', the parameter types are not consistent.", name)); } if (syncMethodInfo != null && method.syncMethodInfo != null) { throw new ArgumentException( String.Format("Invalid declarations for method '{0}', more than one synchronous call found.", name)); } if (asyncBeginMethodInfo != null && method.asyncBeginMethodInfo != null) { throw new ArgumentException( String.Format("Invalid declarations for method '{0}', more than one begin async call found.", name)); } if (asyncEndMethodInfo != null && method.asyncEndMethodInfo != null) { throw new ArgumentException( String.Format("Invalid declarations for method '{0}', more than one end async call found.", name)); } returnType = returnType ?? method.returnType; parameterTypes = parameterTypes ?? method.parameterTypes; syncMethodInfo = syncMethodInfo ?? method.syncMethodInfo; asyncBeginMethodInfo = asyncBeginMethodInfo ?? method.asyncBeginMethodInfo; asyncEndMethodInfo = asyncEndMethodInfo ?? method.asyncEndMethodInfo; }
public void Merge(RpcMethodDescriptor method) { Debug.Assert(method.name == name && method.serviceName == serviceName); if (returnType != null && method.returnType != null && returnType != method.returnType) throw new ArgumentException( String.Format("Invalid declarations for method '{0}', the return types are not consistent.", name)); if (parameterTypes != null && method.parameterTypes != null && !parameterTypes.SequenceEqual(method.parameterTypes)) throw new ArgumentException( String.Format("Invalid declarations for method '{0}', the parameter types are not consistent.", name)); if (syncMethodInfo != null && method.syncMethodInfo != null) throw new ArgumentException( String.Format("Invalid declarations for method '{0}', more than one synchronous call found.", name)); if (asyncBeginMethodInfo != null && method.asyncBeginMethodInfo != null) throw new ArgumentException( String.Format("Invalid declarations for method '{0}', more than one begin async call found.", name)); if (asyncEndMethodInfo != null && method.asyncEndMethodInfo != null) throw new ArgumentException( String.Format("Invalid declarations for method '{0}', more than one end async call found.", name)); returnType = returnType ?? method.returnType; parameterTypes = parameterTypes ?? method.parameterTypes; syncMethodInfo = syncMethodInfo ?? method.syncMethodInfo; asyncBeginMethodInfo = asyncBeginMethodInfo ?? method.asyncBeginMethodInfo; asyncEndMethodInfo = asyncEndMethodInfo ?? method.asyncEndMethodInfo; }
private void InitMethodsFromType(Type type, bool isAttributedMethodsOnly) { foreach (var methodInfo in type.GetMethods()) { RpcMethodAttribute methodAttr = GetMethodAttribute(methodInfo); if (methodAttr != null || !isAttributedMethodsOnly) { RpcMethodDescriptor method = new RpcMethodDescriptor(methodInfo, methodAttr, name); if (methods.ContainsKey(method.Name)) { methods[method.Name].Merge(method); } else { methods.Add(method.Name, method); } } } //for classes checking the public methods is enough, but for an interface we must check the other //interfaces implemented too if (type.IsInterface) { foreach (var interfaceType in type.GetInterfaces()) InitMethodsFromType(interfaceType, isAttributedMethodsOnly); } }
public static Delegate BuildThunk(RpcMethodDescriptor method) { var dynMethod = new DynamicMethod("ServiceThunk", typeof(void), new[] { typeof(object), typeof(IList <RpcMessage.Parameter>), typeof(RpcMessage.Result) }, typeof(ServiceThunkBuilder).Module); ILGenerator ilGen = dynMethod.GetILGenerator(); //verify number of parameters ilGen.Emit(OpCodes.Ldarg_1); //push IList<RpcMessage.Parameter> ilGen.Emit(OpCodes.Ldc_I4, method.ParameterTypes.Length); //push number of expected parameters ilGen.Emit(OpCodes.Ldarg_2); //push RpcMessage.Result ilGen.Emit(OpCodes.Call, typeof(ServiceThunkHelpers).GetMethod("VerifyParameterCount")); Label paramVerifyLabel = ilGen.DefineLabel(); ilGen.Emit(OpCodes.Brtrue, paramVerifyLabel); ilGen.Emit(OpCodes.Ret); ilGen.MarkLabel(paramVerifyLabel); //load the RpcMessage.Result to the stack, for the call to set the result if (method.ReturnType != typeof(void)) { ilGen.Emit(OpCodes.Ldarg_2); } //load the service instance to the stack, for the call to the service instance ilGen.Emit(OpCodes.Ldarg_0); //load the service call parameters to the stack, if they are the wrong type then an error is stored //in the result message and we return early MethodInfo paramIndexer = typeof(IList <RpcMessage.Parameter>).GetProperty("Item").GetGetMethod(); for (int i = 0; i < method.ParameterTypes.Length; ++i) { Type paramType = method.ParameterTypes[i]; //get RpcMessage.Parameter by calling IList indexer ilGen.Emit(OpCodes.Ldarg_1); ilGen.Emit(OpCodes.Ldc_I4, i); ilGen.Emit(OpCodes.Callvirt, paramIndexer); ilGen.Emit(OpCodes.Ldarg_2); //push RpcMessage.Result LocalBuilder paramLocal = ilGen.DeclareLocal(paramType); ilGen.Emit(OpCodes.Ldloca, (short)paramLocal.LocalIndex); //push ref to result local EmitParameter(ilGen, paramType); Label continueLabel = ilGen.DefineLabel(); ilGen.Emit(OpCodes.Brtrue, continueLabel); //success, continue with next parameter //failed, stack cleanup for (int j = 0; j < i; ++j) { ilGen.Emit(OpCodes.Pop); //pop previous parameters } ilGen.Emit(OpCodes.Pop); //pop service instance if (method.ReturnType != typeof(void)) { ilGen.Emit(OpCodes.Pop); //pop RpcMessage.Result } ilGen.Emit(OpCodes.Ret); ilGen.MarkLabel(continueLabel); //now push the actual parameter ready for the actual call ilGen.Emit(OpCodes.Ldloc, paramLocal.LocalIndex); } //now call the actual service instance method ilGen.Emit(OpCodes.Callvirt, method.SyncCallMethod); //get the return value, storing it in the result message. We know the type, no need for any error //checking if (method.ReturnType != typeof(void)) { //check if result is null ilGen.Emit(OpCodes.Ldarg_2); ilGen.Emit(OpCodes.Ldnull); Label nullReturnLabel = ilGen.DefineLabel(); ilGen.Emit(OpCodes.Beq, nullReturnLabel); //convert return value to a message and store it in the result message ThunkHelpers.EmitParameterToMessage(ilGen, method.ReturnType); ilGen.Emit(OpCodes.Call, typeof(RpcMessage.Result).GetProperty("CallResult").GetSetMethod()); ilGen.Emit(OpCodes.Ret); ilGen.MarkLabel(nullReturnLabel); ilGen.Emit(OpCodes.Pop); //pop return value ilGen.Emit(OpCodes.Pop); //pop result message } ilGen.Emit(OpCodes.Ret); return(dynMethod.CreateDelegate(typeof(RpcServer.MethodDelegate))); }