private static void WriteMapImpl(ILGenerator il, Type type, List<MemberInfo> members, FieldBuilder mapField, bool allowNonPublicAccessors, bool isGet)
{
OpCode obj, index, value;
Label fail = il.DefineLabel();
if (mapField == null)
{
index = OpCodes.Ldarg_0;
obj = OpCodes.Ldarg_1;
value = OpCodes.Ldarg_2;
}
else
{
il.DeclareLocal(typeof(int));
index = OpCodes.Ldloc_0;
obj = OpCodes.Ldarg_1;
value = OpCodes.Ldarg_3;
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, mapField);
il.Emit(OpCodes.Ldarg_2);
il.Emit(OpCodes.Ldloca_S, (byte)0);
il.EmitCall(OpCodes.Callvirt, tryGetValue, null);
il.Emit(OpCodes.Brfalse, fail);
}
Label[] labels = new Label[members.Count];
for (int i = 0; i < labels.Length; i++)
{
labels[i] = il.DefineLabel();
}
il.Emit(index);
il.Emit(OpCodes.Switch, labels);
il.MarkLabel(fail);
il.Emit(OpCodes.Ldstr, "name");
il.Emit(OpCodes.Newobj, typeof(ArgumentOutOfRangeException).GetConstructor(new Type[] { typeof(string) }));
il.Emit(OpCodes.Throw);
for (int i = 0; i < labels.Length; i++)
{
il.MarkLabel(labels[i]);
var member = members[i];
bool isFail = true;
FieldInfo field;
PropertyInfo prop;
if((field = member as FieldInfo) != null)
{
il.Emit(obj);
Cast(il, type, true);
if (isGet)
{
il.Emit(OpCodes.Ldfld, field);
if (_IsValueType(field.FieldType)) il.Emit(OpCodes.Box, field.FieldType);
}
else
{
il.Emit(value);
Cast(il, field.FieldType, false);
il.Emit(OpCodes.Stfld, field);
}
il.Emit(OpCodes.Ret);
isFail = false;
}
else if ((prop = member as PropertyInfo) != null)
{
MethodInfo accessor;
if (prop.CanRead && (accessor = isGet ? prop.GetGetMethod(allowNonPublicAccessors) : prop.GetSetMethod(allowNonPublicAccessors)) != null)
{
il.Emit(obj);
Cast(il, type, true);
if (isGet)
{
il.EmitCall(_IsValueType(type) ? OpCodes.Call : OpCodes.Callvirt, accessor, null);
if (_IsValueType(prop.PropertyType)) il.Emit(OpCodes.Box, prop.PropertyType);
}
else
{
il.Emit(value);
Cast(il, prop.PropertyType, false);
il.EmitCall(_IsValueType(type) ? OpCodes.Call : OpCodes.Callvirt, accessor, null);
}
il.Emit(OpCodes.Ret);
isFail = false;
}
}
if (isFail) il.Emit(OpCodes.Br, fail);
}
}