private bool DoValueTypeFixup(FieldInfo memberToFix, ObjectHolder holder, object value)
{
var fieldsTemp = new FieldInfo[4];
FieldInfo[] fields = null;
int currentFieldIndex = 0;
int[] arrayIndex = null;
ValueTypeFixupInfo currFixup = null;
object fixupObj = holder.ObjectValue;
ObjectHolder originalHolder = holder;
Debug.Assert(holder != null, "[TypedReferenceBuilder.ctor]holder!=null");
Debug.Assert(holder.RequiresValueTypeFixup, "[TypedReferenceBuilder.ctor]holder.RequiresValueTypeFixup");
//In order to get a TypedReference, we need to get a list of all of the FieldInfos to
//create the path from our outermost containing object down to the actual field which
//we'd like to set. This loop is used to build up that list.
while (holder.RequiresValueTypeFixup)
{
//Enlarge the array if required (this is actually fairly unlikely as it would require that we
//be nested more than 4 deep.
if ((currentFieldIndex + 1) >= fieldsTemp.Length)
{
var temp = new FieldInfo[fieldsTemp.Length * 2];
Array.Copy(fieldsTemp, 0, temp, 0, fieldsTemp.Length);
fieldsTemp = temp;
}
//Get the fixup information. If we have data for our parent field, add it to our list
//and continue the walk up to find the next outermost containing object. We cache the
//object that we have. In most cases, we could have just grabbed it after this loop finished.
//However, if the outermost containing object is an array, we need the object one further
//down the chain, so we have to do a lot of caching.
currFixup = holder.ValueFixup;
fixupObj = holder.ObjectValue; //Save the most derived
if (currFixup.ParentField != null)
{
FieldInfo parentField = currFixup.ParentField;
ObjectHolder tempHolder = FindObjectHolder(currFixup.ContainerID);
if (tempHolder.ObjectValue == null)
{
break;
}
if (Nullable.GetUnderlyingType(parentField.FieldType) != null)
{
fieldsTemp[currentFieldIndex] = parentField.FieldType.GetField("value", BindingFlags.NonPublic | BindingFlags.Instance);
currentFieldIndex++;
}
fieldsTemp[currentFieldIndex] = parentField;
holder = tempHolder;
currentFieldIndex++;
}
else
{
//If we find an index into an array, save that information.
Debug.Assert(currFixup.ParentIndex != null, "[ObjectManager.DoValueTypeFixup]currFixup.ParentIndex!=null");
holder = FindObjectHolder(currFixup.ContainerID); //find the array to fix.
arrayIndex = currFixup.ParentIndex;
break;
}
}
//If the outermost container isn't an array, we need to grab it. Otherwise, we just need to hang onto
//the boxed object that we already grabbed. We'll assign the boxed object back into the array as the
//last step.
if (!(holder.ObjectValue is Array) && holder.ObjectValue != null)
{
fixupObj = holder.ObjectValue;
Debug.Assert(fixupObj != null, "[ObjectManager.DoValueTypeFixup]FixupObj!=null");
}
if (currentFieldIndex != 0)
{
//MakeTypedReference requires an array of exactly the correct size that goes from the outermost object
//in to the innermost field. We currently have an array of arbitrary size that goes from the innermost
//object outwards. We create an array of the right size and do the copy.
fields = new FieldInfo[currentFieldIndex];
for (int i = 0; i < currentFieldIndex; i++)
{
FieldInfo fieldInfo = fieldsTemp[(currentFieldIndex - 1 - i)];
fields[i] = fieldInfo;
}
Debug.Assert(fixupObj != null, "[ObjectManager.DoValueTypeFixup]fixupObj!=null");
//Make the TypedReference and use it to set the value.
TypedReference typedRef = TypedReference.MakeTypedReference(fixupObj, fields);
if (memberToFix != null)
{
memberToFix.SetValueDirect(typedRef, value);
}
else
{
TypedReference.SetTypedReference(typedRef, value);
}
}
else if (memberToFix != null)
{
FormatterServices.SerializationSetValue(memberToFix, fixupObj, value);
}
//If we have an array index, it means that our outermost container was an array. We don't have
//any way to build a TypedReference into an array, so we'll use the array functions to set the value.
if (arrayIndex != null && holder.ObjectValue != null)
{
((Array)(holder.ObjectValue)).SetValue(fixupObj, arrayIndex);
}
return true;
}