e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
334 lines
14 KiB
C#
334 lines
14 KiB
C#
//----------------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//----------------------------------------------------------------
|
|
|
|
namespace System.Activities.Expressions
|
|
{
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Linq.Expressions;
|
|
using System.Reflection;
|
|
using System.Runtime;
|
|
using System.Text;
|
|
using System.Threading;
|
|
|
|
static class MethodCallExpressionHelper
|
|
{
|
|
public const int FuncCacheCapacity = 500;
|
|
|
|
static void PrepareForVariables(MethodBase methodInfo, ParameterExpression objectArray, Collection<ParameterExpression> variables,
|
|
Collection<Expression> assignVariablesExpressions, Collection<Expression> assignVariablesBackExpressions)
|
|
{
|
|
if (methodInfo != null)
|
|
{
|
|
ParameterInfo[] parameterInfos = methodInfo.GetParameters();
|
|
|
|
for (int i = 0; i < parameterInfos.Length; i++)
|
|
{
|
|
Type parameterType = parameterInfos[i].ParameterType;
|
|
ParameterExpression variable = Expression.Parameter(parameterType.IsByRef ? parameterType.GetElementType() : parameterType, "arg" + i);
|
|
// If variable.Type is NOT a Nullable<T>, we include the call to Convert.ChangeType on the actual parameter.
|
|
if (variable.Type.IsValueType && Nullable.GetUnderlyingType(variable.Type) == null)
|
|
{
|
|
assignVariablesExpressions.Add(Expression.Assign(variable,
|
|
Expression.Convert(
|
|
Expression.Call(typeof(Convert).GetMethod("ChangeType", new Type[] { typeof(object), typeof(Type) }), Expression.ArrayIndex(objectArray, Expression.Constant(i)), Expression.Constant(variable.Type, typeof(Type))),
|
|
variable.Type)));
|
|
}
|
|
else
|
|
{
|
|
assignVariablesExpressions.Add(Expression.Assign(variable,
|
|
Expression.Convert(Expression.ArrayIndex(objectArray, Expression.Constant(i)), variable.Type)));
|
|
}
|
|
if (parameterType.IsByRef)
|
|
{
|
|
if (variable.Type.IsValueType)
|
|
{
|
|
assignVariablesBackExpressions.Add(Expression.Assign(Expression.ArrayAccess(objectArray, Expression.Constant(i)),
|
|
Expression.Convert(variable, typeof(object))));
|
|
}
|
|
else
|
|
{
|
|
assignVariablesBackExpressions.Add(Expression.Assign(Expression.ArrayAccess(objectArray, Expression.Constant(i)),
|
|
variable));
|
|
}
|
|
}
|
|
variables.Add(variable);
|
|
}
|
|
}
|
|
}
|
|
|
|
static MethodCallExpression PrepareForCallExpression(MethodInfo methodInfo, ParameterExpression targetInstance, Collection<ParameterExpression> variables)
|
|
{
|
|
MethodCallExpression callExpression;
|
|
if (!methodInfo.IsStatic)
|
|
{
|
|
callExpression = Expression.Call(Expression.Convert(targetInstance, methodInfo.DeclaringType), methodInfo, variables);
|
|
}
|
|
else
|
|
{
|
|
callExpression = Expression.Call(methodInfo, variables);
|
|
}
|
|
return callExpression;
|
|
}
|
|
|
|
static MethodCallExpression PrepareForCallExpression(MethodInfo methodInfo, ParameterExpression targetInstance,
|
|
Collection<ParameterExpression> variables, out ParameterExpression tempInstance, out Expression assignTempInstanceExpression)
|
|
{
|
|
MethodCallExpression callExpression;
|
|
tempInstance = null;
|
|
assignTempInstanceExpression = null;
|
|
if (!methodInfo.IsStatic)
|
|
{
|
|
if (methodInfo.DeclaringType.IsValueType)
|
|
{
|
|
tempInstance = Expression.Parameter(methodInfo.DeclaringType, "tempInstance");
|
|
assignTempInstanceExpression = Expression.Assign(tempInstance, Expression.Convert(targetInstance, methodInfo.DeclaringType));
|
|
callExpression = Expression.Call(tempInstance,
|
|
methodInfo, variables);
|
|
}
|
|
else
|
|
{
|
|
callExpression = Expression.Call(Expression.Convert(targetInstance, methodInfo.DeclaringType), methodInfo, variables);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
callExpression = Expression.Call(methodInfo, variables);
|
|
}
|
|
return callExpression;
|
|
}
|
|
|
|
static Expression ComposeBlockExpression(Collection<ParameterExpression> variables, Collection<Expression> assignVariables, Expression callExpression,
|
|
Collection<Expression> assignVariablesBack, Type returnType, bool isConstructor, bool valueTypeReference)
|
|
{
|
|
Collection<Expression> expressions = new Collection<Expression>();
|
|
foreach (Expression expression in assignVariables)
|
|
{
|
|
expressions.Add(expression);
|
|
}
|
|
|
|
ParameterExpression result = Expression.Parameter(isConstructor ? returnType : typeof(object), "result");
|
|
variables.Add(result);
|
|
if (returnType != typeof(void))
|
|
{
|
|
Expression resultAssign = null;
|
|
if (!isConstructor && returnType.IsValueType)
|
|
{
|
|
resultAssign = Expression.Assign(result,
|
|
Expression.Convert(callExpression, typeof(object)));
|
|
}
|
|
else
|
|
{
|
|
resultAssign = Expression.Assign(result, callExpression);
|
|
}
|
|
expressions.Add(resultAssign);
|
|
}
|
|
else
|
|
{
|
|
expressions.Add(callExpression);
|
|
}
|
|
foreach (Expression expression in assignVariablesBack)
|
|
{
|
|
expressions.Add(expression);
|
|
}
|
|
|
|
if (!valueTypeReference)
|
|
{
|
|
expressions.Add(result);
|
|
}
|
|
|
|
Expression block = Expression.Block(variables, expressions);
|
|
return block;
|
|
}
|
|
|
|
static Expression ComposeLinqExpression(MethodInfo methodInfo, ParameterExpression targetInstance, ParameterExpression objectArray, Type returnType, bool valueTypeReference)
|
|
{
|
|
Collection<Expression> assignVariablesExpressions = new Collection<Expression>();
|
|
Collection<Expression> assignVariablesBackExpressions = new Collection<Expression>();
|
|
Collection<ParameterExpression> variables = new Collection<ParameterExpression>();
|
|
|
|
PrepareForVariables(methodInfo, objectArray, variables, assignVariablesExpressions, assignVariablesBackExpressions);
|
|
|
|
ParameterExpression tempInstance = null;
|
|
Expression assignTempInstanceExpression = null;
|
|
Expression expression;
|
|
|
|
if (!methodInfo.IsStatic && methodInfo.DeclaringType.IsValueType && valueTypeReference)
|
|
{
|
|
expression = PrepareForCallExpression((MethodInfo)methodInfo, targetInstance, variables, out tempInstance, out assignTempInstanceExpression);
|
|
variables.Add(tempInstance);
|
|
assignVariablesExpressions.Add(assignTempInstanceExpression);
|
|
assignVariablesBackExpressions.Add(Expression.Assign(targetInstance, Expression.Convert(tempInstance, typeof(object))));
|
|
return ComposeBlockExpression(variables, assignVariablesExpressions, expression, assignVariablesBackExpressions, returnType, false, true);
|
|
}
|
|
else
|
|
{
|
|
expression = PrepareForCallExpression((MethodInfo)methodInfo, targetInstance, variables);
|
|
return ComposeBlockExpression(variables, assignVariablesExpressions, expression, assignVariablesBackExpressions, returnType, false, false);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
static Expression ComposeLinqExpression<TResult>(ConstructorInfo constructorInfo, ParameterExpression objectArray)
|
|
{
|
|
Collection<Expression> assignVariablesExpressions = new Collection<Expression>();
|
|
Collection<Expression> assignVariablesBackExpressions = new Collection<Expression>();
|
|
Collection<ParameterExpression> variables = new Collection<ParameterExpression>();
|
|
|
|
PrepareForVariables(constructorInfo, objectArray, variables, assignVariablesExpressions, assignVariablesBackExpressions);
|
|
|
|
NewExpression newExpression;
|
|
if (constructorInfo != null)
|
|
{
|
|
newExpression = Expression.New(constructorInfo, variables);
|
|
}
|
|
else
|
|
{
|
|
newExpression = Expression.New(typeof(TResult));
|
|
}
|
|
return ComposeBlockExpression(variables, assignVariablesExpressions, newExpression, assignVariablesBackExpressions, typeof(TResult), true, false);
|
|
}
|
|
|
|
static Func<object, object[], object> GetFunc(CodeActivityMetadata metadata, MethodInfo methodInfo, bool valueTypeReference = false)
|
|
{
|
|
try
|
|
{
|
|
ParameterExpression targetInstance = Expression.Parameter(typeof(object), "targetInstance");
|
|
ParameterExpression objectArray = Expression.Parameter(typeof(object[]), "arguments");
|
|
Expression block = ComposeLinqExpression(methodInfo, targetInstance, objectArray, methodInfo.ReturnType, valueTypeReference);
|
|
Expression<Func<object, object[], object>> lambdaExpression = Expression.Lambda<Func<object, object[], object>>(block, targetInstance, objectArray);
|
|
return lambdaExpression.Compile();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
metadata.AddValidationError(e.Message);
|
|
return null;
|
|
}
|
|
|
|
}
|
|
|
|
static Func<object[], TResult> GetFunc<TResult>(CodeActivityMetadata metadata, ConstructorInfo constructorInfo)
|
|
{
|
|
try
|
|
{
|
|
ParameterExpression objectArray = Expression.Parameter(typeof(object[]), "arguments");
|
|
Expression block = ComposeLinqExpression<TResult>(constructorInfo, objectArray);
|
|
Expression<Func<object[], TResult>> lambdaExpression = Expression.Lambda<Func<object[], TResult>>(block, objectArray);
|
|
return lambdaExpression.Compile();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
metadata.AddValidationError(e.Message);
|
|
return null;
|
|
}
|
|
|
|
}
|
|
|
|
internal static bool NeedRetrieve(MethodBase newMethod, MethodBase oldMethod, Delegate func)
|
|
{
|
|
if (newMethod == null)
|
|
{
|
|
return false;
|
|
}
|
|
if ((newMethod == oldMethod) && (func != null))
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
internal static Func<object, object[], object> GetFunc(CodeActivityMetadata metadata, MethodInfo methodInfo,
|
|
MruCache<MethodInfo, Func<object, object[], object>> cache, ReaderWriterLockSlim locker, bool valueTypeReference = false)
|
|
{
|
|
Func<object, object[], object> func = null;
|
|
locker.EnterWriteLock();
|
|
try
|
|
{
|
|
cache.TryGetValue(methodInfo, out func);
|
|
}
|
|
finally
|
|
{
|
|
locker.ExitWriteLock();
|
|
}
|
|
if (func == null)
|
|
{
|
|
func = GetFunc(metadata, methodInfo, valueTypeReference);
|
|
locker.EnterWriteLock();
|
|
try
|
|
{
|
|
//MruCache has on ContainsKey(), so we use TryGetValue()
|
|
Func<object, object[], object> result = null;
|
|
if (!cache.TryGetValue(methodInfo, out result))
|
|
{
|
|
cache.Add(methodInfo, func);
|
|
}
|
|
else
|
|
{
|
|
func = result;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
locker.ExitWriteLock();
|
|
}
|
|
}
|
|
return func;
|
|
}
|
|
|
|
internal static Func<object[], TResult> GetFunc<TResult>(CodeActivityMetadata metadata, ConstructorInfo constructorInfo,
|
|
MruCache<ConstructorInfo, Func<object[], TResult>> cache, ReaderWriterLockSlim locker)
|
|
{
|
|
Func<object[], TResult> func = null;
|
|
if (constructorInfo != null)
|
|
{
|
|
locker.EnterWriteLock();
|
|
try
|
|
{
|
|
cache.TryGetValue(constructorInfo, out func);
|
|
}
|
|
finally
|
|
{
|
|
locker.ExitWriteLock();
|
|
}
|
|
}
|
|
if (func == null)
|
|
{
|
|
func = GetFunc<TResult>(metadata, constructorInfo);
|
|
if (constructorInfo != null)
|
|
{
|
|
locker.EnterWriteLock();
|
|
try
|
|
{
|
|
//MruCache has on ContainsKey(), so we use TryGetValue()
|
|
Func<object[], TResult> result = null;
|
|
if (!cache.TryGetValue(constructorInfo, out result))
|
|
{
|
|
cache.Add(constructorInfo, func);
|
|
}
|
|
else
|
|
{
|
|
func = result;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
locker.ExitWriteLock();
|
|
}
|
|
}
|
|
}
|
|
return func;
|
|
}
|
|
}
|
|
}
|