Mercurial > pub > bltoolkit
diff Source/Aspects/Builders/OverloadAspectBuilder.cs @ 0:f990fcb411a9
Копия текущей версии из github
author | cin |
---|---|
date | Thu, 27 Mar 2014 21:46:09 +0400 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Aspects/Builders/OverloadAspectBuilder.cs Thu Mar 27 21:46:09 2014 +0400 @@ -0,0 +1,219 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; + +using BLToolkit.Properties; +using BLToolkit.Reflection; +using BLToolkit.Reflection.Emit; +using BLToolkit.TypeBuilder; +using BLToolkit.TypeBuilder.Builders; + +namespace BLToolkit.Aspects.Builders +{ + public class OverloadAspectBuilder: AbstractTypeBuilderBase + { + private readonly string _overloadedMethodName; + private readonly Type[] _parameterTypes; + + public OverloadAspectBuilder(string overloadedMethodName, Type[] parameterTypes) + { + _overloadedMethodName = overloadedMethodName; + _parameterTypes = parameterTypes; + } + + public override int GetPriority(BuildContext context) + { + return TypeBuilderConsts.Priority.OverloadAspect; + } + + public override bool IsCompatible(BuildContext context, IAbstractTypeBuilder typeBuilder) + { + if (context.IsBuildStep) + return false; + + AbstractTypeBuilderList list = new AbstractTypeBuilderList(2); + + list.Add(this); + list.Add(typeBuilder); + + BuildStep step = context.Step; + + try + { + context.Step = BuildStep.Build; + + return typeBuilder.IsApplied(context, list); + } + finally + { + context.Step = step; + } + } + + public override bool IsApplied(BuildContext context, AbstractTypeBuilderList builders) + { + if (context == null) throw new ArgumentNullException("context"); + + return context.IsBuildStep && context.BuildElement == BuildElement.AbstractMethod; + } + + protected override void BuildAbstractMethod() + { + MethodInfo currentMethod = Context.CurrentMethod; + string methodName = _overloadedMethodName ?? currentMethod.Name; + MethodInfo overloadedMethod = GetOverloadedMethod(methodName); + + if (overloadedMethod == null) + { + throw new TypeBuilderException(string.Format( + Resources.OverloadAspectBuilder_NoOverloadedMethod, + Context.Type.FullName, methodName)); + } + + EmitHelper emit = Context.MethodBuilder.Emitter; + List<ParameterInfo> parameters = new List<ParameterInfo>(currentMethod.GetParameters()); + + if (!overloadedMethod.IsStatic) + emit.ldarg_0.end(); + + foreach (ParameterInfo param in overloadedMethod.GetParameters()) + { + ParameterInfo currentMethodParameter = null; + foreach (ParameterInfo p in parameters) + { + if (p.Name != param.Name) + continue; + + currentMethodParameter = p; + parameters.Remove(p); + break; + } + + if (currentMethodParameter != null) + { + emit.ldarg(currentMethodParameter); + } + else + { + Type type = param.ParameterType; + bool isRef = false; + + if (type.IsByRef) + { + type = type.GetElementType(); + isRef = true; + } + + if (type.IsValueType && !type.IsPrimitive) + { + LocalBuilder localBuilder = emit.DeclareLocal(type); + + emit + .ldloca (localBuilder) + .initobj (type) + ; + + if (isRef) + emit.ldloca (localBuilder); + else + emit.ldloc (localBuilder); + + } + else + { + if ((param.Attributes & ParameterAttributes.HasDefault) == 0 || + !emit.LoadWellKnownValue(param.DefaultValue)) + { + emit.LoadInitValue(type); + } + + if (isRef) + { + LocalBuilder localBuilder = emit.DeclareLocal(type); + + emit + .stloc (localBuilder) + .ldloca (localBuilder) + ; + } + } + } + } + + // Finally, call the method we override. + // + if (overloadedMethod.IsStatic || overloadedMethod.IsFinal) + emit.call (overloadedMethod); + else + emit.callvirt (overloadedMethod); + + if (currentMethod.ReturnType != typeof(void)) + emit.stloc(Context.ReturnValue); + } + + private MethodInfo GetOverloadedMethod(string methodName) + { + MethodInfo currentMethod = Context.CurrentMethod; + MethodInfo bestMatch = null; + int bestMatchParametersCount = -1; + ParameterInfo[] currentMethodParameters = currentMethod.GetParameters(); + + if (_parameterTypes != null) + { + bestMatch = Context.Type.GetMethod(methodName, _parameterTypes); + return bestMatch != null && MatchParameters(currentMethodParameters, bestMatch.GetParameters()) >= 0? bestMatch: null; + } + + const BindingFlags bindingFlags = BindingFlags.Static | BindingFlags.Instance| BindingFlags.Public | BindingFlags.NonPublic; + + foreach (MethodInfo m in Context.Type.GetMethods(bindingFlags)) + { + if (m.IsPrivate || m.Name != methodName || m.IsGenericMethod != currentMethod.IsGenericMethod) + continue; + + if (!TypeHelper.CompareParameterTypes(m.ReturnType, currentMethod.ReturnType)) + continue; + + if (m.IsDefined(typeof(OverloadAttribute), true)) + continue; + + ParameterInfo[] overloadedMethodParameters = m.GetParameters(); + if (overloadedMethodParameters.Length <= bestMatchParametersCount) + continue; + + int matchedParameters = MatchParameters(overloadedMethodParameters, currentMethodParameters); + if (matchedParameters <= bestMatchParametersCount) + continue; + + bestMatchParametersCount = matchedParameters; + bestMatch = m; + } + + return bestMatch; + } + + private static int MatchParameters(ParameterInfo[] parametersToMatch, ParameterInfo[] existingParameters) + { + int matchedParameters = 0; + List<ParameterInfo> existingParametersList = new List<ParameterInfo>(existingParameters); + foreach (ParameterInfo param in parametersToMatch) + { + foreach (ParameterInfo existing in existingParametersList) + { + if (existing.Name != param.Name) + continue; + + if (!TypeHelper.CompareParameterTypes(param.ParameterType, existing.ParameterType)) + return -1; + + ++matchedParameters; + existingParametersList.Remove(existing); + break; + } + } + + return matchedParameters; + } + } +} \ No newline at end of file