protected override Expression VisitDynamic(DynamicExpression node)
{
DynamicMetaObjectBinder metaBinder = node.Binder as DynamicMetaObjectBinder;
if (metaBinder == null)
{
// don't rewrite non meta-binder nodes, we can't compose them
return(node);
}
// gather the real arguments for the new dynamic site node
var args = node.Arguments;
bool foundSideEffectingArgs = false;
List <Expression> inputs = new List <Expression>();
// parameter mapping is 1 List<ComboParameterMappingInfo> for each meta binder, the inner list
// contains the mapping info for each particular binder
List <BinderMappingInfo> binders = new List <BinderMappingInfo>();
List <ParameterMappingInfo> myInfo = new List <ParameterMappingInfo>();
int actionCount = 0;
for (int i = 0; i < args.Count; i++)
{
Expression e = args[i];
if (!foundSideEffectingArgs)
{
// attempt to combine the arguments...
Expression rewritten = Visit(e);
ComboDynamicSiteExpression combo = rewritten as ComboDynamicSiteExpression;
ConstantExpression ce;
if (combo != null)
{
// an action expression we can combine with our own expression
// remember how many actions we have so far - if any of our children consume
// actions their offset is bumped up
int baseActionCount = actionCount;
foreach (BinderMappingInfo comboInfo in combo.Binders)
{
List <ParameterMappingInfo> newInfo = new List <ParameterMappingInfo>();
foreach (ParameterMappingInfo info in comboInfo.MappingInfo)
{
if (info.IsParameter)
{
// all of the inputs from the child now become ours
newInfo.Add(ParameterMappingInfo.Parameter(inputs.Count));
inputs.Add(combo.Inputs[info.ParameterIndex]);
}
else if (info.IsAction)
{
newInfo.Add(ParameterMappingInfo.Action(info.ActionIndex + baseActionCount));
actionCount++;
}
else
{
Debug.Assert(info.Constant != null);
// constants can just flow through
newInfo.Add(info);
}
}
binders.Add(new BinderMappingInfo(comboInfo.Binder, newInfo));
}
myInfo.Add(ParameterMappingInfo.Action(actionCount++));
}
else if ((ce = rewritten as ConstantExpression) != null)
{
// we can hoist the constant into the combo
myInfo.Add(ParameterMappingInfo.Fixed(ce));
}
else if (IsSideEffectFree(rewritten))
{
// we can treat this as an input parameter
myInfo.Add(ParameterMappingInfo.Parameter(inputs.Count));
inputs.Add(rewritten);
}
else
{
// this argument is doing something we don't understand - we have to leave
// it as is (an input we consume) and all the remaining arguments need to be
// evaluated normally as this could have side effects on them.
foundSideEffectingArgs = true;
myInfo.Add(ParameterMappingInfo.Parameter(inputs.Count));
inputs.Add(e);
}
}
else
{
// we've already seen an argument which may have side effects, don't do
// any more combinations.
myInfo.Add(ParameterMappingInfo.Parameter(inputs.Count));
inputs.Add(e);
}
}
binders.Add(new BinderMappingInfo(metaBinder, myInfo));
// TODO: Remove any duplicate inputs (e.g. locals being fed in multiple times)
return(new ComboDynamicSiteExpression(node.Type, binders, inputs.ToArray()));
}