public override void Emit (EmitContext ec)
{
//
// Use same anonymous method implementation for scenarios where same
// code is used from multiple blocks, e.g. field initializers
//
if (method == null) {
//
// Delay an anonymous method definition to avoid emitting unused code
// for unreachable blocks or expression trees
//
method = DoCreateMethodHost (ec);
method.Define ();
}
bool is_static = (method.ModFlags & Modifiers.STATIC) != 0;
if (is_static && am_cache == null) {
//
// Creates a field cache to store delegate instance if it's not generic
//
if (!method.MemberName.IsGeneric) {
TypeContainer parent = method.Parent.PartialContainer;
int id = parent.Fields == null ? 0 : parent.Fields.Count;
var cache_type = storey != null && storey.Mutator != null ? storey.Mutator.Mutate (type) : type;
am_cache = new Field (parent, new TypeExpression (cache_type, loc),
Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
new MemberName (CompilerGeneratedClass.MakeName (null, "f", "am$cache", id), loc), null);
am_cache.Define ();
parent.AddField (am_cache);
} else {
// TODO: Implement caching of generated generic static methods
//
// Idea:
//
// Some extra class is needed to capture variable generic type
// arguments. Maybe we could re-use anonymous types, with a unique
// anonymous method id, but they are quite heavy.
//
// Consider : "() => typeof(T);"
//
// We need something like
// static class Wrap<Tn, Tm, DelegateType> {
// public static DelegateType cache;
// }
//
// We then specialize local variable to capture all generic parameters
// and delegate type, e.g. "Wrap<Ta, Tb, DelegateTypeInst> cache;"
//
}
}
Label l_initialized = ec.DefineLabel ();
if (am_cache != null) {
ec.Emit (OpCodes.Ldsfld, am_cache.Spec);
ec.Emit (OpCodes.Brtrue_S, l_initialized);
}
//
// Load method delegate implementation
//
if (is_static) {
ec.Emit (OpCodes.Ldnull);
} else if (storey != null) {
Expression e = storey.GetStoreyInstanceExpression (ec).Resolve (new ResolveContext (ec.MemberContext));
if (e != null)
e.Emit (ec);
} else {
ec.Emit (OpCodes.Ldarg_0);
}
var delegate_method = method.Spec;
if (storey != null && storey.MemberName.IsGeneric) {
TypeSpec t = storey.Instance.Type;
//
// Mutate anonymous method instance type if we are in nested
// hoisted generic anonymous method storey
//
if (ec.CurrentAnonymousMethod != null &&
ec.CurrentAnonymousMethod.Storey != null &&
ec.CurrentAnonymousMethod.Storey.Mutator != null) {
t = storey.Mutator.Mutate (t);
}
ec.Emit (OpCodes.Ldftn, TypeBuilder.GetMethod (t.GetMetaInfo (), (MethodInfo) delegate_method.GetMetaInfo ()));
} else {
if (delegate_method.IsGeneric)
delegate_method = delegate_method.MakeGenericMethod (method.TypeParameters);
ec.Emit (OpCodes.Ldftn, delegate_method);
}
var constructor_method = Delegate.GetConstructor (ec.MemberContext.Compiler, ec.CurrentType, type);
ec.Emit (OpCodes.Newobj, constructor_method);
if (am_cache != null) {
ec.Emit (OpCodes.Stsfld, am_cache.Spec);
ec.MarkLabel (l_initialized);
ec.Emit (OpCodes.Ldsfld, am_cache.Spec);
}
}