|
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.Activities.Statements
{
using System;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime;
using System.Linq.Expressions;
// Inverted Template Method pattern. MethodExecutor is the base class for executing a method; created by MethodResolver.
// Private concrete implementations are created by MethodResolver, but this is the "public" API used by InvokeMethod.
abstract class MethodExecutor
{
// Used for creating tracing messages w/ DisplayName
protected Activity invokingActivity;
// We may still need to know targetType if we're autocreating targets during ExecuteMethod
Type targetType;
InArgument targetObject;
Collection<Argument> parameters;
RuntimeArgument returnObject;
public MethodExecutor(Activity invokingActivity, Type targetType, InArgument targetObject,
Collection<Argument> parameters, RuntimeArgument returnObject)
{
Fx.Assert(invokingActivity != null, "Must provide invokingActivity");
Fx.Assert(targetType != null || (targetObject != null), "Must provide targetType or targetObject");
Fx.Assert(parameters != null, "Must provide parameters");
// returnObject is optional
this.invokingActivity = invokingActivity;
this.targetType = targetType;
this.targetObject = targetObject;
this.parameters = parameters;
this.returnObject = returnObject;
}
public abstract bool MethodIsStatic { get; }
protected abstract IAsyncResult BeginMakeMethodCall(AsyncCodeActivityContext context, object target, AsyncCallback callback, object state);
protected abstract void EndMakeMethodCall(AsyncCodeActivityContext context, IAsyncResult result);
static bool HaveParameterArray(ParameterInfo[] parameters)
{
if (parameters.Length > 0)
{
ParameterInfo last = parameters[parameters.Length - 1];
return last.GetCustomAttributes(typeof(ParamArrayAttribute), true).GetLength(0) > 0;
}
else
{
return false;
}
}
protected object[] EvaluateAndPackParameters(CodeActivityContext context, MethodInfo method,
bool usingAsyncPattern)
{
ParameterInfo[] formalParameters = method.GetParameters();
int formalParamCount = formalParameters.Length;
object[] actualParameters = new object[formalParamCount];
if (usingAsyncPattern)
{
formalParamCount -= 2;
}
bool haveParameterArray = HaveParameterArray(formalParameters);
for (int i = 0; i < formalParamCount; i++)
{
if (i == formalParamCount - 1 && !usingAsyncPattern && haveParameterArray)
{
int paramArrayCount = this.parameters.Count - formalParamCount + 1;
// If params are given explicitly, that's okay.
if (paramArrayCount == 1 && TypeHelper.AreTypesCompatible(this.parameters[i].ArgumentType,
formalParameters[i].ParameterType))
{
actualParameters[i] = this.parameters[i].Get<object>(context);
}
else
{
// Otherwise, pack them into an array for the reflection call.
actualParameters[i] =
Activator.CreateInstance(formalParameters[i].ParameterType, paramArrayCount);
for (int j = 0; j < paramArrayCount; j++)
{
((object[])actualParameters[i])[j] = this.parameters[i + j].Get<object>(context);
}
}
continue;
}
actualParameters[i] = parameters[i].Get<object>(context);
}
return actualParameters;
}
[SuppressMessage(FxCop.Category.Usage, FxCop.Rule.InstantiateArgumentExceptionsCorrectly, Justification = "TargetObject is a parameter to InvokeMethod, rather than this specific method.")]
public IAsyncResult BeginExecuteMethod(AsyncCodeActivityContext context, AsyncCallback callback, object state)
{
object targetInstance = null;
if (!this.MethodIsStatic)
{
targetInstance = this.targetObject.Get(context);
if (targetInstance == null)
{
throw FxTrace.Exception.ArgumentNull("TargetObject");
}
}
return BeginMakeMethodCall(context, targetInstance, callback, state); // defer to concrete instance for sync/async variations
}
public void EndExecuteMethod(AsyncCodeActivityContext context, IAsyncResult result)
{
EndMakeMethodCall(context, result); // defer to concrete instance for sync/async variations
}
[SuppressMessage("Reliability", "Reliability108:IsFatalRule",
Justification = "We need throw out all exceptions from method invocation.")]
internal object InvokeAndUnwrapExceptions(Func<object, object[], object> func, object targetInstance, object[] actualParameters)
{
try
{
return func(targetInstance, actualParameters);
}
catch (Exception e)
{
if (TD.InvokedMethodThrewExceptionIsEnabled())
{
TD.InvokedMethodThrewException(this.invokingActivity.DisplayName, e.ToString());
}
throw FxTrace.Exception.AsError(e);
}
}
public void SetOutArgumentAndReturnValue(ActivityContext context, object state, object[] actualParameters)
{
for (int index = 0; index < parameters.Count; index++)
{
if (parameters[index].Direction != ArgumentDirection.In)
{
parameters[index].Set(context, actualParameters[index]);
}
}
if (this.returnObject != null)
{
this.returnObject.Set(context, state);
}
}
public void Trace(Activity parent)
{
if (this.MethodIsStatic)
{
if (TD.InvokeMethodIsStaticIsEnabled())
{
TD.InvokeMethodIsStatic(parent.DisplayName);
}
}
else
{
if (TD.InvokeMethodIsNotStaticIsEnabled())
{
TD.InvokeMethodIsNotStatic(parent.DisplayName);
}
}
}
}
}
|