private ConstructorInfo generateType(Guid id, IInterceptor interceptor, ConstructorInfo ctor) { if (interceptor == null) { throw new ArgumentNullException(nameof(interceptor)); } var baseType = ctor?.DeclaringType ?? throw new ArgumentNullException(nameof(ctor)); var name = baseType.Name; var genericNameIndex = name.IndexOf('`'); if (genericNameIndex != -1) { name = name.Substring(0, genericNameIndex); } var settings = new ProxyAttribute(); if (!settings.Enabled) { throw new NotSupportedException("This class does not support proxying."); } var typeBuilder = proxyModule.Value.DefineType($"ProxyFor{name}_{id.ToString().Replace("-", "")}", TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit, baseType); var interceptorField = typeBuilder.DefineField("interceptor", typeof(IInterceptor), FieldAttributes.Private | FieldAttributes.InitOnly); generateCtor(typeBuilder, interceptorField, ctor); if (baseType.GetMethods() .Where(methodInfo => methodInfo.DeclaringType != typeof(object) && methodInfo.IsVirtual && !methodInfo.IsGenericMethod) .Count(methodInfo => tryToOverrideMethod(settings, interceptor, typeBuilder, interceptorField, methodInfo)) == 0) { throw new ArgumentException($"Unable to proxy any member for this type: {baseType.FullName}"); } return(typeBuilder.CreateTypeInfo().GetConstructors().Single()); }
private bool tryToOverrideMethod(ProxyAttribute settings, IInterceptor interceptor, TypeBuilder typeBuilder, FieldBuilder interceptorField, MethodInfo methodInfo) { settings = methodInfo.GetCustomAttribute <ProxyAttribute>() ?? settings; if (!settings.Enabled) { return(false); } var returnType = methodInfo.ReturnType; if (typeof(Task) == returnType) { returnType = typeof(void); } else if (typeof(Task).IsAssignableFrom(returnType)) { if (!returnType.IsGenericType || returnType.GetGenericTypeDefinition() != typeof(Task <>)) { return(handleInvalidType(settings.InvalidTypeHandling, typeBuilder, methodInfo, $"Unable to create proxy class. Unknown Task return type for method: {methodInfo.Name}")); } returnType = returnType.GetGenericArguments().Single(); } if (returnType != typeof(void) && !interceptor.Serializer.CanSerialize(returnType)) { return(handleInvalidType(settings.InvalidTypeHandling, typeBuilder, methodInfo, $"Unable to create proxy class. Invalid return type for method: {methodInfo.Name}")); } var @params = methodInfo.GetParameters(); if (@params.Any(param => param.IsOut || !interceptor.Serializer.CanSerialize(param.ParameterType))) { return(handleInvalidType(settings.InvalidTypeHandling, typeBuilder, methodInfo, $"Unable to create proxy class. Invalid argument type for method: {methodInfo.Name}")); } var methodBuilder = typeBuilder.DefineMethod( methodInfo.Name, methodInfo.Attributes, methodInfo.CallingConvention, methodInfo.ReturnType, @params.Select(param => param.ParameterType).ToArray()); var il = methodBuilder.GetILGenerator(); //load interceptor field il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, interceptorField); //load fire and forget boolean value var fireAndForget = settings.FireAndForget && (methodInfo.ReturnType == typeof(void) || methodInfo.ReturnType == typeof(Task)); il.Emit(fireAndForget ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); //load method name il.Emit(OpCodes.Ldstr, methodInfo.Name); //load new tuple array if (@params.Length == 0) { // ReSharper disable once PossibleNullReferenceException il.Emit(OpCodes.Call, typeof(Array).GetMethod("Empty").MakeGenericMethod(typeof(Tuple <Type, object>))); } else { il.Emit(OpCodes.Ldc_I4, @params.Length); il.Emit(OpCodes.Newarr, typeof(Tuple <Type, object>)); for (var index = 0; index < @params.Length; index++) { il.Emit(OpCodes.Dup); //load param index il.Emit(OpCodes.Ldc_I4, index); var param = @params[index]; //load typeof parameter il.Emit(OpCodes.Ldtoken, param.ParameterType); il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", new[] { typeof(RuntimeTypeHandle) })); //load argument il.Emit(OpCodes.Ldarg, index + 1); il.Emit(OpCodes.Box, param.ParameterType); //make tuple of parameter type and argument il.Emit(OpCodes.Newobj, typeof(Tuple <Type, object>).GetConstructors().First()); //set tuple value to loaded index in the tuple array il.Emit(OpCodes.Stelem_Ref); } } // ReSharper disable once PossibleNullReferenceException il.Emit(OpCodes.Callvirt, typeof(IInterceptor) .GetMethod(nameof(IInterceptor.RemoteInvoke)) .MakeGenericMethod(returnType == typeof(void) ? typeof(object) : returnType)); if (methodInfo.ReturnType == typeof(void)) { il.Emit(OpCodes.Callvirt, typeof(Task).GetMethod(nameof(Task.Wait), Type.EmptyTypes)); } else if (!typeof(Task).IsAssignableFrom(methodInfo.ReturnType)) { // ReSharper disable once PossibleNullReferenceException il.Emit(OpCodes.Callvirt, typeof(Task <>).MakeGenericType(returnType).GetProperty("Result").GetMethod); } il.Emit(OpCodes.Ret); typeBuilder.DefineMethodOverride(methodBuilder, methodInfo); return(true); }