ObjectReader CreateReader(Type type)
{
if (type == typeof(string)) {
// String contents are written in the object creation section,
// not into the field value section; so there's nothing to read here.
return delegate {};
}
bool isArray = type.IsArray;
if (isArray) {
if (type.GetArrayRank() != 1)
throw new NotSupportedException();
type = type.GetElementType();
if (!type.IsValueType) {
return delegate (DeserializationContext context, object arrayInstance) {
object[] array = (object[])arrayInstance;
for (int i = 0; i < array.Length; i++) {
array[i] = context.ReadObject();
}
};
} else if (type == typeof(byte)) {
return delegate (DeserializationContext context, object arrayInstance) {
byte[] array = (byte[])arrayInstance;
BinaryReader binaryReader = context.Reader;
int pos = 0;
int bytesRead;
do {
bytesRead = binaryReader.Read(array, pos, array.Length - pos);
pos += bytesRead;
} while (bytesRead > 0);
if (pos != array.Length)
throw new EndOfStreamException();
};
}
}
var fields = GetSerializableFields(type);
if (fields.Count == 0) {
// The reader has nothing to do for this object.
return delegate { };
}
DynamicMethod dynamicMethod = new DynamicMethod(
(isArray ? "ReadArray_" : "Read_") + type.Name,
MethodAttributes.Public | MethodAttributes.Static,
CallingConventions.Standard,
typeof(void), new [] { typeof(DeserializationContext), typeof(object) },
type,
true);
ILGenerator il = dynamicMethod.GetILGenerator();
var reader = il.DeclareLocal(typeof(BinaryReader));
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, readerField);
il.Emit(OpCodes.Stloc, reader); // reader = context.reader;
if (isArray) {
var instance = il.DeclareLocal(type.MakeArrayType());
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Castclass, type.MakeArrayType());
il.Emit(OpCodes.Stloc, instance); // instance = (type[])arg_1;
// for (int i = 0; i < instance.Length; i++) read &instance[i];
var loopStart = il.DefineLabel();
var loopHead = il.DefineLabel();
var loopVariable = il.DeclareLocal(typeof(int));
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Stloc, loopVariable); // loopVariable = 0
il.Emit(OpCodes.Br, loopHead); // goto loopHead;
il.MarkLabel(loopStart);
if (type.IsEnum || type.IsPrimitive) {
if (type.IsEnum) {
type = type.GetEnumUnderlyingType();
}
Debug.Assert(type.IsPrimitive);
il.Emit(OpCodes.Ldloc, instance); // instance
il.Emit(OpCodes.Ldloc, loopVariable); // instance, loopVariable
ReadPrimitiveValue(il, reader, type); // instance, loopVariable, value
switch (Type.GetTypeCode(type)) {
case TypeCode.Boolean:
case TypeCode.SByte:
case TypeCode.Byte:
il.Emit(OpCodes.Stelem_I1); // instance[loopVariable] = value;
break;
case TypeCode.Char:
case TypeCode.Int16:
case TypeCode.UInt16:
il.Emit(OpCodes.Stelem_I2); // instance[loopVariable] = value;
break;
case TypeCode.Int32:
case TypeCode.UInt32:
il.Emit(OpCodes.Stelem_I4); // instance[loopVariable] = value;
break;
case TypeCode.Int64:
case TypeCode.UInt64:
il.Emit(OpCodes.Stelem_I8); // instance[loopVariable] = value;
break;
case TypeCode.Single:
il.Emit(OpCodes.Stelem_R4); // instance[loopVariable] = value;
break;
case TypeCode.Double:
il.Emit(OpCodes.Stelem_R8); // instance[loopVariable] = value;
break;
default:
throw new NotSupportedException("Unknown primitive type " + type);
}
} else {
il.Emit(OpCodes.Ldloc, instance); // instance
il.Emit(OpCodes.Ldloc, loopVariable); // instance, loopVariable
il.Emit(OpCodes.Ldelema, type); // instance[loopVariable]
EmitReadValueType(il, reader, type);
}
il.Emit(OpCodes.Ldloc, loopVariable); // loopVariable
il.Emit(OpCodes.Ldc_I4_1); // loopVariable, 1
il.Emit(OpCodes.Add); // loopVariable+1
il.Emit(OpCodes.Stloc, loopVariable); // loopVariable++;
il.MarkLabel(loopHead);
il.Emit(OpCodes.Ldloc, loopVariable); // loopVariable
il.Emit(OpCodes.Ldloc, instance); // loopVariable, instance
il.Emit(OpCodes.Ldlen); // loopVariable, instance.Length
il.Emit(OpCodes.Conv_I4);
il.Emit(OpCodes.Blt, loopStart); // if (loopVariable < instance.Length) goto loopStart;
} else if (type.IsValueType) {
// boxed value type
il.Emit(OpCodes.Ldarg_1); // instance
il.Emit(OpCodes.Unbox, type); // &(Type)instance
if (type.IsEnum || type.IsPrimitive) {
if (type.IsEnum) {
type = type.GetEnumUnderlyingType();
}
Debug.Assert(type.IsPrimitive);
ReadPrimitiveValue(il, reader, type); // &(Type)instance, value
switch (Type.GetTypeCode(type)) {
case TypeCode.Boolean:
case TypeCode.SByte:
case TypeCode.Byte:
il.Emit(OpCodes.Stind_I1);
break;
case TypeCode.Char:
case TypeCode.Int16:
case TypeCode.UInt16:
il.Emit(OpCodes.Stind_I2);
break;
case TypeCode.Int32:
case TypeCode.UInt32:
il.Emit(OpCodes.Stind_I4);
break;
case TypeCode.Int64:
case TypeCode.UInt64:
il.Emit(OpCodes.Stind_I8);
break;
case TypeCode.Single:
il.Emit(OpCodes.Stind_R4);
break;
case TypeCode.Double:
il.Emit(OpCodes.Stind_R8);
break;
default:
throw new NotSupportedException("Unknown primitive type " + type);
}
} else {
EmitReadValueType(il, reader, type);
}
} else {
// reference type
var instance = il.DeclareLocal(type);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Castclass, type);
il.Emit(OpCodes.Stloc, instance); // instance = (type)arg_1;
foreach (FieldInfo field in fields) {
EmitReadField(il, reader, instance, field); // read instance.Field
}
}
il.Emit(OpCodes.Ret);
return (ObjectReader)dynamicMethod.CreateDelegate(typeof(ObjectReader));
}