protected void EmitCall(EmitContext ec, Expression binder, Arguments arguments, bool isStatement)
{
//
// This method generates all internal infrastructure for a dynamic call. The
// reason why it's quite complicated is the mixture of dynamic and anonymous
// methods. Dynamic itself requires a temporary class (ContainerX) and anonymous
// methods can generate temporary storey as well (AnonStorey). Handling MVAR
// type parameters rewrite is non-trivial in such case as there are various
// combinations possible therefore the mutator is not straightforward. Secondly
// we need to keep both MVAR(possibly VAR for anon storey) and type VAR to emit
// correct Site field type and its access from EmitContext.
//
int dyn_args_count = arguments == null ? 0 : arguments.Count;
int default_args = isStatement ? 1 : 2;
var module = ec.Module;
bool has_ref_out_argument = false;
var targs = new TypeExpression[dyn_args_count + default_args];
targs[0] = new TypeExpression(module.PredefinedTypes.CallSite.TypeSpec, loc);
TypeExpression[] targs_for_instance = null;
TypeParameterMutator mutator;
var site_container = ec.CreateDynamicSite();
if (context_mvars != null)
{
TypeParameters tparam;
TypeContainer sc = site_container;
do
{
tparam = sc.CurrentTypeParameters;
sc = sc.Parent;
} while (tparam == null);
mutator = new TypeParameterMutator(context_mvars, tparam);
if (!ec.IsAnonymousStoreyMutateRequired)
{
targs_for_instance = new TypeExpression[targs.Length];
targs_for_instance[0] = targs[0];
}
}
else
{
mutator = null;
}
for (int i = 0; i < dyn_args_count; ++i)
{
Argument a = arguments[i];
if (a.ArgType == Argument.AType.Out || a.ArgType == Argument.AType.Ref)
{
has_ref_out_argument = true;
}
var t = a.Type;
// Convert any internal type like dynamic or null to object
if (t.Kind == MemberKind.InternalCompilerType)
{
t = ec.BuiltinTypes.Object;
}
if (targs_for_instance != null)
{
targs_for_instance[i + 1] = new TypeExpression(t, loc);
}
if (mutator != null)
{
t = t.Mutate(mutator);
}
targs[i + 1] = new TypeExpression(t, loc);
}
TypeExpr del_type = null;
TypeExpr del_type_instance_access = null;
if (!has_ref_out_argument)
{
string d_name = isStatement ? "Action" : "Func";
TypeSpec te = null;
Namespace type_ns = module.GlobalRootNamespace.GetNamespace("System", true);
if (type_ns != null)
{
te = type_ns.LookupType(module, d_name, dyn_args_count + default_args, LookupMode.Normal, loc);
}
if (te != null)
{
if (!isStatement)
{
var t = type;
if (t.Kind == MemberKind.InternalCompilerType)
{
t = ec.BuiltinTypes.Object;
}
if (targs_for_instance != null)
{
targs_for_instance[targs_for_instance.Length - 1] = new TypeExpression(t, loc);
}
if (mutator != null)
{
t = t.Mutate(mutator);
}
targs[targs.Length - 1] = new TypeExpression(t, loc);
}
del_type = new GenericTypeExpr(te, new TypeArguments(targs), loc);
if (targs_for_instance != null)
{
del_type_instance_access = new GenericTypeExpr(te, new TypeArguments(targs_for_instance), loc);
}
else
{
del_type_instance_access = del_type;
}
}
}
//
// Create custom delegate when no appropriate predefined delegate has been found
//
Delegate d;
if (del_type == null)
{
TypeSpec rt = isStatement ? ec.BuiltinTypes.Void : type;
Parameter[] p = new Parameter[dyn_args_count + 1];
p[0] = new Parameter(targs[0], "p0", Parameter.Modifier.NONE, null, loc);
var site = ec.CreateDynamicSite();
int index = site.Containers == null ? 0 : site.Containers.Count;
if (mutator != null)
{
rt = mutator.Mutate(rt);
}
for (int i = 1; i < dyn_args_count + 1; ++i)
{
p[i] = new Parameter(targs[i], "p" + i.ToString("X"), arguments[i - 1].Modifier, null, loc);
}
d = new Delegate(site, new TypeExpression(rt, loc),
Modifiers.INTERNAL | Modifiers.COMPILER_GENERATED,
new MemberName("Container" + index.ToString("X")),
new ParametersCompiled(p), null);
d.CreateContainer();
d.DefineContainer();
d.Define();
d.PrepareEmit();
site.AddTypeContainer(d);
//
// Add new container to inflated site container when the
// member cache already exists
//
if (site.CurrentType is InflatedTypeSpec && index > 0)
{
site.CurrentType.MemberCache.AddMember(d.CurrentType);
}
del_type = new TypeExpression(d.CurrentType, loc);
if (targs_for_instance != null)
{
del_type_instance_access = null;
}
else
{
del_type_instance_access = del_type;
}
}
else
{
d = null;
}
var site_type_decl = new GenericTypeExpr(module.PredefinedTypes.CallSiteGeneric.TypeSpec, new TypeArguments(del_type), loc);
var field = site_container.CreateCallSiteField(site_type_decl, loc);
if (field == null)
{
return;
}
if (del_type_instance_access == null)
{
var dt = d.CurrentType.DeclaringType.MakeGenericType(module, context_mvars.Types);
del_type_instance_access = new TypeExpression(MemberCache.GetMember(dt, d.CurrentType), loc);
}
var instanceAccessExprType = new GenericTypeExpr(module.PredefinedTypes.CallSiteGeneric.TypeSpec,
new TypeArguments(del_type_instance_access), loc);
if (instanceAccessExprType.ResolveAsType(ec.MemberContext) == null)
{
return;
}
bool inflate_using_mvar = context_mvars != null && ec.IsAnonymousStoreyMutateRequired;
TypeSpec gt;
if (inflate_using_mvar || context_mvars == null)
{
gt = site_container.CurrentType;
}
else
{
gt = site_container.CurrentType.MakeGenericType(module, context_mvars.Types);
}
// When site container already exists the inflated version has to be
// updated manually to contain newly created field
if (gt is InflatedTypeSpec && site_container.AnonymousMethodsCounter > 1)
{
var tparams = gt.MemberDefinition.TypeParametersCount > 0 ? gt.MemberDefinition.TypeParameters : TypeParameterSpec.EmptyTypes;
var inflator = new TypeParameterInflator(module, gt, tparams, gt.TypeArguments);
gt.MemberCache.AddMember(field.InflateMember(inflator));
}
FieldExpr site_field_expr = new FieldExpr(MemberCache.GetMember(gt, field), loc);
BlockContext bc = new BlockContext(ec.MemberContext, null, ec.BuiltinTypes.Void);
Arguments args = new Arguments(1);
args.Add(new Argument(binder));
StatementExpression s = new StatementExpression(new SimpleAssign(site_field_expr, new Invocation(new MemberAccess(instanceAccessExprType, "Create"), args)));
using (ec.With(BuilderContext.Options.OmitDebugInfo, true)) {
if (s.Resolve(bc))
{
Statement init = new If(new Binary(Binary.Operator.Equality, site_field_expr, new NullLiteral(loc)), s, loc);
init.Emit(ec);
}
args = new Arguments(1 + dyn_args_count);
args.Add(new Argument(site_field_expr));
if (arguments != null)
{
int arg_pos = 1;
foreach (Argument a in arguments)
{
if (a is NamedArgument)
{
// Name is not valid in this context
args.Add(new Argument(a.Expr, a.ArgType));
}
else
{
args.Add(a);
}
if (inflate_using_mvar && a.Type != targs[arg_pos].Type)
{
a.Expr.Type = targs[arg_pos].Type;
}
++arg_pos;
}
}
Expression target = new DelegateInvocation(new MemberAccess(site_field_expr, "Target", loc).Resolve(bc), args, loc).Resolve(bc);
if (target != null)
{
target.Emit(ec);
}
}
}