diff Source/Aspects/Builders/InterceptorAspectBuilder.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/InterceptorAspectBuilder.cs	Thu Mar 27 21:46:09 2014 +0400
@@ -0,0 +1,341 @@
+using System;
+using System.Reflection;
+using System.Reflection.Emit;
+
+namespace BLToolkit.Aspects.Builders
+{
+	using Reflection;
+	using TypeBuilder.Builders;
+
+	public class InterceptorAspectBuilder : AbstractTypeBuilderBase
+	{
+		public InterceptorAspectBuilder(
+			Type interceptorType, InterceptType interceptType, string configString, int priority, bool localInterceptor)
+		{
+			_interceptorType  = interceptorType;
+			_interceptType    = interceptType;
+			_configString     = configString;
+			_priority         = priority;
+			_localInterceptor = localInterceptor;
+		}
+
+		private readonly Type          _interceptorType;
+		private readonly InterceptType _interceptType;
+		private readonly string        _configString;
+		private readonly int           _priority;
+		private readonly bool          _localInterceptor;
+
+		private          FieldBuilder  _interceptorField;
+		private          LocalBuilder  _infoField;
+
+		public override int GetPriority(BuildContext context)
+		{
+			return _priority;
+		}
+
+		public override bool IsApplied(BuildContext context, AbstractTypeBuilderList builders)
+		{
+			if (_interceptorType == null && _interceptType == 0)
+				return false;
+
+			foreach (var builder in builders)
+			{
+				var interceptor = builder as InterceptorAspectBuilder;
+
+				if (interceptor != null)
+				{
+					if (interceptor._interceptorType == null && interceptor._interceptType == 0)
+						return false;
+
+					if (builder == this)
+						break;
+				}
+			}
+
+			if (context.IsMethodOrProperty) switch (context.Step)
+			{
+				case BuildStep.Begin:   return true;
+				case BuildStep.Before:  return (_interceptType & InterceptType.BeforeCall) != 0;
+				case BuildStep.After:   return (_interceptType & InterceptType.AfterCall)  != 0;
+				case BuildStep.Catch:   return (_interceptType & InterceptType.OnCatch)    != 0;
+				case BuildStep.Finally: return (_interceptType & InterceptType.OnFinally)  != 0;
+				case BuildStep.End:     return true;
+			}
+
+			return false;
+		}
+
+		public override bool IsCompatible(BuildContext context, IAbstractTypeBuilder typeBuilder)
+		{
+			var builder = typeBuilder as InterceptorAspectBuilder;
+
+			return builder == null || _interceptorType != builder._interceptorType;
+		}
+
+		public override void Build(BuildContext context)
+		{
+			if (context.Step == BuildStep.Begin || context.Step == BuildStep.End)
+			{
+				base.Build(context);
+				return;
+			}
+
+			Context = context;
+
+			var emit = Context.MethodBuilder.Emitter;
+
+			// Push ref & out parameters.
+			//
+			var parameters = Context.CurrentMethod.GetParameters();
+
+			for (var i = 0; i < parameters.Length; i++)
+			{
+				var p = parameters[i];
+
+				if (!p.ParameterType.IsByRef)
+					continue;
+
+				emit
+					.ldloc      (_infoField)
+					.callvirt   (typeof(InterceptCallInfo).GetProperty("ParameterValues").GetGetMethod())
+					.ldc_i4     (i)
+					.ldargEx    (p, true)
+					.stelem_ref
+					.end()
+					;
+			}
+
+			// Push return value.
+			//
+			if (Context.ReturnValue != null)
+			{
+				emit
+					.ldloc          (_infoField)
+					.ldloc          (Context.ReturnValue)
+					.boxIfValueType (Context.CurrentMethod.ReturnType)
+					.callvirt       (typeof(InterceptCallInfo).GetProperty("ReturnValue").GetSetMethod())
+					;
+			}
+
+			// Set Exception.
+			//
+			if (Context.Step == BuildStep.Catch)
+			{
+				emit
+					.ldloc(_infoField)
+					.ldloc(Context.Exception)
+					.callvirt(typeof(InterceptCallInfo).GetProperty("Exception").GetSetMethod())
+					;
+			}
+
+			// Set intercept result.
+			//
+			emit
+				.ldloc    (_infoField)
+				.ldc_i4   ((int)InterceptResult.Continue)
+				.callvirt (typeof(InterceptCallInfo).GetProperty("InterceptResult").GetSetMethod())
+				;
+
+			// Set intercept type.
+			//
+			InterceptType interceptType;
+
+			switch (Context.Step)
+			{
+				case BuildStep.Before:  interceptType = InterceptType.BeforeCall; break;
+				case BuildStep.After:   interceptType = InterceptType.AfterCall;  break;
+				case BuildStep.Catch:   interceptType = InterceptType.OnCatch;    break;
+				case BuildStep.Finally: interceptType = InterceptType.OnFinally;  break;
+				default:
+					throw new InvalidOperationException();
+			}
+
+			emit
+				.ldloc    (_infoField)
+				.ldc_i4   ((int)interceptType)
+				.callvirt (typeof(InterceptCallInfo).GetProperty("InterceptType").GetSetMethod())
+
+			// Call interceptor.
+			//
+				.LoadField(_interceptorField)
+				.ldloc    (_infoField)
+				.callvirt (typeof(IInterceptor), "Intercept", typeof(InterceptCallInfo))
+				;
+
+			// Pop return value.
+			//
+			if (Context.ReturnValue != null)
+			{
+				emit
+					.ldloc          (_infoField)
+					.callvirt       (typeof(InterceptCallInfo).GetProperty("ReturnValue").GetGetMethod())
+					.CastFromObject (Context.CurrentMethod.ReturnType)
+					.stloc          (Context.ReturnValue)
+					;
+			}
+
+			// Pop ref & out parameters.
+			//
+			for (var i = 0; i < parameters.Length; i++)
+			{
+				var p = parameters[i];
+
+				if (!p.ParameterType.IsByRef)
+					continue;
+
+				var type = p.ParameterType.GetElementType();
+
+				emit
+					.ldarg          (p)
+					.ldloc          (_infoField)
+					.callvirt       (typeof(InterceptCallInfo).GetProperty("ParameterValues").GetGetMethod())
+					.ldc_i4         (i)
+					.ldelem_ref
+					.CastFromObject (type)
+					.stind          (type)
+					;
+			}
+
+			// Check InterceptResult
+			emit
+				.ldloc    (_infoField)
+				.callvirt (typeof(InterceptCallInfo).GetProperty("InterceptResult").GetGetMethod())
+				.ldc_i4   ((int)InterceptResult.Return)
+				.beq      (Context.ReturnLabel)
+				;
+		}
+
+		private static int _methodCounter;
+
+		private LocalBuilder GetInfoField()
+		{
+			var field = Context.GetItem<LocalBuilder>("$BLToolkit.InfoField");
+
+			if (field == null)
+			{
+				_methodCounter++;
+
+				// Create MethodInfo field.
+				//
+				var methodInfo = Context.CreatePrivateStaticField(
+					"_methodInfo$" + Context.CurrentMethod.Name + _methodCounter, typeof(CallMethodInfo));
+
+				var emit = Context.MethodBuilder.Emitter;
+
+				var checkMethodInfo = emit.DefineLabel();
+
+				emit
+					.LoadField (methodInfo)
+					.brtrue_s  (checkMethodInfo)
+					.call      (typeof(MethodBase), "GetCurrentMethod")
+					.castclass (typeof(MethodInfo))
+					.newobj    (TypeHelper.GetConstructor(typeof(CallMethodInfo), typeof(MethodInfo)))
+					.stsfld    (methodInfo)
+					.MarkLabel (checkMethodInfo)
+					;
+
+				// Create & initialize the field.
+				//
+				field = emit.DeclareLocal(typeof(InterceptCallInfo));
+
+				emit
+					.newobj   (TypeHelper.GetDefaultConstructor(typeof(InterceptCallInfo)))
+					.dup
+					.ldarg_0
+					.callvirt (typeof(InterceptCallInfo).GetProperty("Object").GetSetMethod())
+
+					.dup
+					.LoadField(methodInfo)
+					.callvirt (typeof(InterceptCallInfo).GetProperty("CallMethodInfo").GetSetMethod())
+					;
+
+				var parameters = Context.CurrentMethod.GetParameters();
+
+				for (var i = 0; i < parameters.Length; i++)
+				{
+					var p = parameters[i];
+
+					if (p.ParameterType.IsByRef)
+						continue;
+
+					emit
+						.dup
+						.callvirt   (typeof(InterceptCallInfo).GetProperty("ParameterValues").GetGetMethod())
+						.ldc_i4     (i)
+						.ldargEx    (p, true)
+						.stelem_ref
+						.end()
+						;
+				}
+
+				emit.stloc(field);
+
+				Context.Items.Add("$BLToolkit.MethodInfo", methodInfo);
+				Context.Items.Add("$BLToolkit.InfoField",  field);
+			}
+
+			return field;
+		}
+
+		private FieldBuilder GetInterceptorField()
+		{
+			var fieldName = "_interceptor$" + _interceptorType.FullName + "$_" + Context.CurrentMethod.Name + _methodCounter;
+			var field     = Context.GetField(fieldName);
+
+			if (field == null)
+			{
+				// Create MethodInfo field.
+				//
+				field = _localInterceptor? 
+					Context.CreatePrivateField      (fieldName, typeof(IInterceptor)):
+					Context.CreatePrivateStaticField(fieldName, typeof(IInterceptor));
+
+				var emit = Context.MethodBuilder.Emitter;
+
+				var checkInterceptor = emit.DefineLabel();
+				var methodInfo       = Context.GetItem<FieldBuilder>("$BLToolkit.MethodInfo");
+
+				emit
+					.LoadField (field)
+					.brtrue_s  (checkInterceptor)
+					;
+
+					if (!field.IsStatic)
+						emit.ldarg_0.end();
+
+				emit
+					.newobj    (TypeHelper.GetDefaultConstructor(_interceptorType))
+					.castclass (typeof(IInterceptor))
+					;
+
+				if (field.IsStatic)
+					emit.stsfld(field);
+				else
+					emit.stfld(field);
+
+				emit
+					.LoadField (field)
+					.LoadField (methodInfo)
+					.ldstrEx   (_configString ?? "")
+					.callvirt  (typeof(IInterceptor), "Init", typeof(CallMethodInfo), typeof(string))
+
+					.MarkLabel (checkInterceptor)
+					;
+			}
+
+			return field;
+		}
+
+		protected override void BeginMethodBuild()
+		{
+			_infoField        = GetInfoField();
+			_interceptorField = GetInterceptorField();
+		}
+
+		protected override void EndMethodBuild()
+		{
+			Context.Items.Remove("$BLToolkit.MethodInfo");
+			Context.Items.Remove("$BLToolkit.InfoField");
+		}
+	}
+}