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, mName); if (mMethods.ContainsKey(method.Name)) { mMethods[method.Name].Merge(method); } else { mMethods.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.mName == mName && method.mServiceName == mServiceName); if (mReturnType != null && method.mReturnType != null && mReturnType != method.mReturnType) { throw new ArgumentException( String.Format("Invalid declarations for method '{0}', the return types are not consistent.", mName)); } if (mParameterTypes != null && method.mParameterTypes != null && !mParameterTypes.SequenceEqual(method.mParameterTypes)) { throw new ArgumentException( String.Format("Invalid declarations for method '{0}', the parameter types are not consistent.", mName)); } if (mSyncMethodInfo != null && method.mSyncMethodInfo != null) { throw new ArgumentException( String.Format("Invalid declarations for method '{0}', more than one synchronous call found.", mName)); } if (mAsyncBeginMethodInfo != null && method.mAsyncBeginMethodInfo != null) { throw new ArgumentException( String.Format("Invalid declarations for method '{0}', more than one begin async call found.", mName)); } if (mAsyncEndMethodInfo != null && method.mAsyncEndMethodInfo != null) { throw new ArgumentException( String.Format("Invalid declarations for method '{0}', more than one end async call found.", mName)); } mReturnType = mReturnType ?? method.mReturnType; mParameterTypes = mParameterTypes ?? method.mParameterTypes; mSyncMethodInfo = mSyncMethodInfo ?? method.mSyncMethodInfo; mAsyncBeginMethodInfo = mAsyncBeginMethodInfo ?? method.mAsyncBeginMethodInfo; mAsyncEndMethodInfo = mAsyncEndMethodInfo ?? method.mAsyncEndMethodInfo; }
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))); }