/// <summary> /// Generates IL to read a single item from the stream and leaves the result on the stack. /// </summary> /// <param name="type">The <see cref="Type"/> of item to read.</param> /// <param name="args">The arguments for generating the il.</param> void GenerateReadTypeIL(Type type, GenerateArgs args) { MethodInfo method = GetTypeMethod(type, TypeMethodType.Deserialize); DynamicMethodHelper il = args.il; bool dynamic = IsDynamic && (type.IsValueType == false); // Variable names const string valueVar = "_readValue"; const string dynamicTypeIndexVar = "_typeIndex"; const string dynamicTypeVar = "_dynamicType"; const string dynamicTypeNameVar = "_dynamicTypeName"; const string dynamicTypeResolvedLabel = "_dynamicTypeResolved"; const string dynamicTypeNotNullLabel = "_dynamicTypeNotNull"; const string dynamicTypeDoneLabel = "_dynamicTypeDone"; il.BeginScope(); { if (dynamic) { il.DeclareLocal(dynamicTypeIndexVar, typeof(byte)); il.DeclareLocal(valueVar, typeof(object)); il.DeclareLocal(dynamicTypeNameVar, typeof(string)); il.DeclareLocal(dynamicTypeVar, typeof(Type)); // Read type index il.PushLocal(args.streamVar); il.CallMethod(GetTypeMethod(typeof(byte), TypeMethodType.Deserialize)); il.PopLocal(dynamicTypeIndexVar); // if (typeIndex == -1) goto :dynamicInstanceNull il.PushLocal(dynamicTypeIndexVar); il.PushInt(SerializerHeaders.NullVersion); il.GotoIfNotEqual(dynamicTypeNotNullLabel); // return null // goto :dynamicTypeDone il.PushNull(); il.Goto(dynamicTypeDoneLabel); // :dynamicTypeNotNull il.MarkLabel(dynamicTypeNotNullLabel); // Get type info for typeIndex il.PushLocal(args.nameTableVar); il.PushLocal(dynamicTypeIndexVar); il.PushLocalAsRef(dynamicTypeVar); il.PushLocalAsRef(dynamicTypeNameVar); il.CallMethod(typeof(TypeNameTable).GetMethod("GetTypeInfo")); // If (type != null) goto :typeResolved il.PushLocal(dynamicTypeVar); il.GotoIfTrue(dynamicTypeResolvedLabel); // Call Type.GetType to resolve type. We must do this // in the context of the type being deserialized to // get the appropriate visibility permissions il.PushLocal(dynamicTypeNameVar); il.PushBool(true); il.CallMethod(typeof(Type).GetMethod( "GetType", BindingFlags.Static | BindingFlags.Public, null, new [] { typeof(string), typeof(bool) }, null )); il.PopLocal(dynamicTypeVar); // Save the resolved type back to the type table so // subsequent instances of the same type don't have to // do it again il.PushLocal(args.nameTableVar); il.PushLocal(dynamicTypeIndexVar); il.PushLocal(dynamicTypeVar); il.CallMethod(typeof(TypeNameTable).GetMethod("SetResolvedType")); // :typeResolved il.MarkLabel(dynamicTypeResolvedLabel); // Create an empty instance of the resolved type il.PushLocal(dynamicTypeVar); il.PushBool(true); il.CallMethod(typeof(Activator).GetMethod( "CreateInstance", new [] { typeof(Type), typeof(bool) } )); il.PopLocal(valueVar); // Call the serializer to read it il.PushLocalAsRef(valueVar); il.PushArg(args.dataArg); il.CallMethod(method); il.Pop(); // return (baseType)dynamicInstance il.PushLocal(valueVar); il.Cast(type); // :dynamicTypeDone il.MarkLabel(dynamicTypeDoneLabel); } else if (method.DeclaringType == typeof(IPrimitiveReader)) { // return reader.ReadXXXX() il.PushLocal(args.streamVar); il.CallMethod(method); } else //if (type.IsClass) { // Create empty instance of type il.DeclareLocal(valueVar, typeof(object)); if (type.IsClass) { il.NewObject(valueVar, type, Type.EmptyTypes); } // Call the serializer to read it il.PushLocalAsRef(valueVar); il.PushArg(args.dataArg); il.CallMethod(method); il.Pop(); // Ignore return value // return (type)instance il.PushLocal(valueVar); if (type.IsClass) { il.Cast(type); } else { il.UnboxValueType(type); } } } il.EndScope(); }
/// <summary> /// Generates IL to deserialize the property from a stream /// </summary> /// <param name="args"> /// <para>The arguments used to generate the read il.</para> /// </param> public void GenerateReadIL(GenerateArgs args) { #region Names const string loopStart = "loopStart"; const string loopEnd = "loopEnd"; const string propVar = "propValue"; const string elemVar = "elemVar"; const string indexVar = "loopIndex"; const string instanceVar = "instance"; const string countVar = "count"; const string collectionNotNull = "collectionNotNull"; const string skipObsoleteLabel = "skipObsolete"; #endregion DynamicMethodHelper il = args.il; if (this.attribute.ReadMethod != null) { // Call custom handler MethodInfo method = this.OwnerType.GetMethod( this.attribute.ReadMethod, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, null, new [] { typeof(IPrimitiveReader) }, null ); if (method == null) { throw new ApplicationException(string.Format( "Failed to locate method {0}.{1} to read property {2}", this.OwnerType.Name, this.attribute.ReadMethod, this.Name )); } il.PushArg(args.instanceArg); il.PushLocal(args.streamVar); il.CallMethod(method); return; } il.DeclareLocal(instanceVar, this.OwnerType); il.DeclareLocal(propVar, this.Type); il.PushArg(args.instanceArg); il.PopLocal(instanceVar); // Create loop for collection types if (IsCollection) { il.DeclareLocal(indexVar, typeof(int)); il.DeclareLocal(countVar, typeof(int)); il.DeclareLocal(elemVar, this.elementType); // countVar = reader.ReadInt32() il.PushLocal(args.streamVar); il.CallMethod(GetTypeMethod(typeof(int), TypeMethodType.Deserialize)); il.PopLocal(countVar); // ; Set prop to null if length < 0 // if (countVar >= 0) goto collectionNotNull il.PushLocal(countVar); il.PushInt(0); il.GotoIfGreaterOrEqual(collectionNotNull); // propVar = null // goto loopEnd il.PushNull(); il.PopLocal(propVar); il.Goto(loopEnd); // :collectionNotNull // if (indexer) // propVar = instanceVar // else if (array) // propVar = new(countVar) // else // propVar = new() // end if il.MarkLabel(collectionNotNull); if (this.classType != null) { il.CopyLocal(instanceVar, propVar); } else if (IsArray) { il.PushLocal(countVar); il.NewObject(propVar, new [] { typeof(int) }); } else { il.NewObject(propVar); } // indexVar = 0 // :loopStart // if (indexVar == countVar) goto loopEnd il.PushInt(0); il.PopLocal(indexVar); il.MarkLabel(loopStart); il.PushLocal(indexVar); il.PushLocal(countVar); il.GotoIfEqual(loopEnd); } // If this is a dictionary then do special handling to add the element // If not then just read the value and use generic code to store it if (IsDictionary) { // propVar.Add(ReadKey(), ReadValue()) il.PushLocal(propVar); il.Cast(this.dictionaryInterfaceType); this.GenerateReadTypeIL(this.dictionaryKeyType, args); this.GenerateReadTypeIL(this.dictionaryValueType, args); il.CallMethod(this.collectionAddMethod); } else { GenerateReadTypeIL(this.elementType, args); il.PopLocal(IsCollection ? elemVar : propVar); } // If this is a collection then add the element and loop if (IsCollection) { switch (this.serializationType & PropertySerializationType.CollectionMask) { case PropertySerializationType.Dictionary: // Already handled break; case PropertySerializationType.Array: // push propValue ; arg 1 (this) // push loopIndex ; arg 2 // push elemValue ; arg 3 // call SetValue il.BeginCallMethod(propVar, "SetValue", new [] { typeof(object), typeof(int) }); il.PushLocalAsObject(elemVar); il.PushLocal(indexVar); il.CallMethod(); break; case PropertySerializationType.List: // push propValue ; arg 1 (this) // push elemValue ; arg 2 // call Add il.PushLocal(propVar); il.Cast(this.collectionInterfaceType); il.PushLocal(elemVar); il.CallMethod(this.collectionAddMethod); break; } // indexVar++ // goto loopStart // :loopEnd il.IncrementLocal(indexVar); il.Goto(loopStart); il.MarkLabel(loopEnd); } // Set property/field value // This isn't required for indexers. if (this.property != null) { if (this.property is PropertyInfo) { il.PushLocal(instanceVar); il.PushLocal(propVar); il.CallMethod(((PropertyInfo)this.property).GetSetMethod(true)); } else // FieldInfo { il.SetField(instanceVar, propVar, (FieldInfo)this.property); } } if (this.attribute.ObsoleteVersion > 0) { il.MarkLabel(skipObsoleteLabel); } }
/// <summary> /// Generates IL for serializing the property /// </summary> /// <param name="args"> /// <para>The arguments used to generate the il.</para> /// </param> public void GenerateWriteIL(GenerateArgs args) { #region Names const string loopConditionLabel = "loopStart"; const string loopEndLabel = "loopEnd"; const string propVar = "propValue"; const string elemVar = "elemValue"; const string instanceVar = "instance"; const string collectionNotNullLabel = "collectionNotNullLabel"; const string countVar = "collectionCount"; const string enumVar = "enumerator"; const string dictEnumVar = "dictEnumerator"; const string dictKeyVar = "dictKey"; const string dictValueVar = "dictValue"; const string enumerableVar = "enumerable"; #endregion DynamicMethodHelper il = args.il; if (this.attribute.WriteMethod != null) { // Call custom handler MethodInfo method = this.OwnerType.GetMethod( this.attribute.WriteMethod, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, null, new [] { typeof(IPrimitiveWriter) }, null ); if (method == null) { throw new ApplicationException(string.Format( "Failed to locate method {0}.{1} to write property {2}", this.OwnerType.Name, this.attribute.ReadMethod, this.Name )); } il.PushArg(args.instanceArg); il.PushLocal(args.streamVar); il.CallMethod(method); return; } il.DeclareLocal(instanceVar, this.OwnerType); il.DeclareLocal(propVar, this.Type); il.PushArg(args.instanceArg); il.PopLocal(instanceVar); // Get property value and store in local if (this.classType != null) { // For indexer load the instance itself il.PushLocal(instanceVar); } else if (this.property is PropertyInfo) { il.PushLocal(instanceVar); il.CallMethod(((PropertyInfo)this.property).GetGetMethod(true)); } else // FieldInfo { // push instance // load_field il.GetField(instanceVar, (FieldInfo)this.property); } // store propValue il.PopLocal(propVar); // If this is a collection and null then store -1 length // If this is a non-null collection then emit loop code if (IsCollection) { il.DeclareLocal(countVar, typeof(int)); il.DeclareLocal(enumVar, typeof(IEnumerator)); il.DeclareLocal(enumerableVar, typeof(IEnumerable)); il.DeclareLocal(elemVar, this.elementType); // push propValue // push null // branch_if_equal collectionNullLabel // branch collectionNotNullLabel il.PushLocal(propVar); il.GotoIfTrue(collectionNotNullLabel); // ; collection is null so write -1 // push writer // push -1 // call IPrimitiveWriter.Write(int) // goto loopEnd il.PushLocal(args.streamVar); il.PushInt(-1); il.CallMethod(GetTypeMethod(typeof(int), TypeMethodType.Serialize)); il.Goto(loopEndLabel); // :collectionNotNullLabel il.MarkLabel(collectionNotNullLabel); // countVar = collection.Length/Count if (IsArray) { il.CallMethod(propVar, "get_Length"); } else { il.PushLocal(propVar); il.Cast(this.collectionInterfaceType); il.CallMethod(this.collectionCountMethod); } il.PopLocal(countVar); // writer.Write(countVar) il.PushLocal(args.streamVar); il.PushLocal(countVar); il.CallMethod(GetTypeMethod(typeof(int), TypeMethodType.Serialize)); // enumerable = propVar // enumVar = enumerable.GetEnumerator() il.CopyLocal(propVar, enumerableVar); il.CallMethod(enumerableVar, "GetEnumerator"); il.PopLocal(enumVar); if (IsDictionary) { il.DeclareLocal(dictEnumVar, typeof(IDictionaryEnumerator)); il.DeclareLocal(dictKeyVar, this.dictionaryKeyType); il.DeclareLocal(dictValueVar, this.dictionaryValueType); il.CopyLocal(enumVar, dictEnumVar); } // :loopConditionLable // if (enumVar.MoveNext == false) goto loopEndLabel il.MarkLabel(loopConditionLabel); il.CallMethod(enumVar, "MoveNext"); il.GotoIfFalse(loopEndLabel); // if (!dictionary) elemVar = enumVar.Current if (IsDictionary == false) { il.CallMethod(enumVar, "get_Current"); il.PopLocalFromObject(elemVar); } } // For dictionary properties serialize the key and value // For everything else serialize the element as-is if (IsDictionary) { // push elemValue // call get_Key // serialize key il.CallMethod(dictEnumVar, "get_Key"); this.GenerateWriteTypeIL(this.dictionaryKeyType, args); // push elemValue // call get_Value // serialize value il.CallMethod(dictEnumVar, "get_Value"); GenerateWriteTypeIL(this.dictionaryValueType, args); } else if (IsCollection) { // push elemValue // serialize elemValue il.PushLocalAsObject(elemVar); GenerateWriteTypeIL(this.elementType, args); } else { il.PushLocalAsObject(propVar); GenerateWriteTypeIL(this.elementType, args); } // Complete loop instructions for collection if (IsCollection) { // branch loopConditionLabel // :loopEnd il.Goto(loopConditionLabel); il.MarkLabel(loopEndLabel); } }
/// <summary> /// Generates IL to write a single value from the stack to the stream /// </summary> /// <param name="type">Type of the object on the stack</param> /// <param name="args">The generate args.</param> void GenerateWriteTypeIL(Type type, GenerateArgs args) { MethodInfo method = GetTypeMethod(type, TypeMethodType.Serialize); const string valueVar = "writeValue"; const string valueDoneLabel = "_writeValueDone"; DynamicMethodHelper il = args.il; bool dynamic = IsDynamic && (type.IsValueType == false); il.BeginScope(); { il.DeclareLocal(valueVar, type); il.PopLocalFromObject(valueVar); if (dynamic) { const string notNullLabel = "_dynamicNotNull"; // If value is a reference type then serialize // NullVersion (-1) and do nothing else. No need to // save a type name or anything. If not null then // add type to name table and write the type byte // before calling serializer to write the instance il.PushLocal(valueVar); il.GotoIfTrue(notNullLabel); il.PushLocal(args.streamVar); il.PushInt(-1); il.CallMethod(GetTypeMethod(typeof(byte), TypeMethodType.Serialize)); il.Goto(valueDoneLabel); il.MarkLabel(notNullLabel); // push writer for Write(byte) call il.PushLocal(args.streamVar); // push nameTable for Add(Type) call il.DebugWriteNamedLocal(args.nameTableVar); il.PushLocal(args.nameTableVar); // push (object)value // call object.GetType() il.PushLocal(valueVar); il.Cast(typeof(object)); il.CallMethod(typeof(object).GetMethod("GetType")); // push this.Version il.PushInt(this.Version); // call TypeNameTable.Add(Type, version) il.CallMethod(typeof(TypeNameTable).GetMethod("Add", new [] { typeof(Type), typeof(int) })); // Return type byte // call IPrimitiveWriter.Write(byte) il.CallMethod(GetTypeMethod(typeof(byte), TypeMethodType.Serialize)); } if (method.DeclaringType == typeof(IPrimitiveWriter)) { // push IPrimitiveWriter ; arg 1 for method call // push elemValue ; arg 2 for method call il.PushLocal(args.streamVar); il.PushLocal(valueVar); } else { // push value // push TypeSerializationArgs il.PushLocal(valueVar); il.PushArg(args.dataArg); } il.CallMethod(method); if (dynamic) { il.MarkLabel(valueDoneLabel); } } il.EndScope(); }
public void GenerateCompareIL(DynamicMethodHelper il, int xIndex, int yIndex) { #region Names const string instanceX = "instanceX"; const string propVarX = "propValueX"; const string enumerableX = "enumerableX"; const string enumVarX = "enumX"; const string dictEnumVarX = "dictEnumX"; const string instanceY = "instanceY"; const string propVarY = "propValueY"; const string enumerableY = "enumerableY"; const string enumVarY = "enumY"; const string dictEnumVarY = "dictEnumY"; const string endFalseLabel = "logBadPropertyLabel"; const string endTrueLabel = "endTrue"; const string endLabel = "endLabel"; const string loopStart = "loopStart"; const string loopEnd = "loopEnd"; #endregion il.DebugWriteLine("Comparing property " + this.Name); il.DeclareLocal(instanceX, this.OwnerType); il.PushArg(xIndex); il.PopLocal(instanceX); il.DeclareLocal(instanceY, this.OwnerType); il.PushArg(yIndex); il.PopLocal(instanceY); il.DeclareLocal(propVarX, this.Type); il.DeclareLocal(propVarY, this.Type); if (this.classType != null) { il.CopyLocal(instanceX, propVarX); il.CopyLocal(instanceY, propVarY); } else if (this.property is PropertyInfo) { PropertyInfo pi = (PropertyInfo)this.property; MethodInfo getMethod = pi.GetGetMethod(true); il.CallMethod(instanceX, getMethod); il.PopLocal(propVarX); il.CallMethod(instanceY, getMethod); il.PopLocal(propVarY); } else { il.GetField(instanceX, this.property as FieldInfo); il.PopLocal(propVarX); il.GetField(instanceY, this.property as FieldInfo); il.PopLocal(propVarY); } // For ref types, if both null or the same object then return true // For value types, this is a value comparison Type t = IsCollection ? (IsDictionary ? null : this.elementType) : this.Type; bool doDefault = true; if (t != null && t.IsValueType) { MethodInfo mi = t.GetMethod("op_Equality", new [] { t, t }); if (mi != null) { il.PushLocal(propVarX); il.PushLocal(propVarY); il.CallMethod(mi); il.GotoIfFalse(endTrueLabel); doDefault = false; } else if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) { Type it = t.GetGenericArguments()[0]; mi = typeof(Nullable).GetMethod("Equals", BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly); mi = mi.MakeGenericMethod(it); il.PushLocal(propVarX); il.PushLocal(propVarY); il.CallMethod(mi); il.GotoIfFalse(endTrueLabel); doDefault = false; } } if (doDefault) { il.PushLocal(propVarX); il.PushLocal(propVarY); il.GotoIfEqual(endTrueLabel); } if (IsCollection) { il.DeclareLocal(enumerableX, typeof(IEnumerable)); il.DeclareLocal(enumVarX, typeof(IEnumerator)); il.DeclareLocal(enumerableY, typeof(IEnumerable)); il.DeclareLocal(enumVarY, typeof(IEnumerator)); il.CopyLocal(propVarX, enumerableX); il.CallMethod(enumerableX, "GetEnumerator"); il.PopLocal(enumVarX); il.CopyLocal(propVarY, enumerableX); il.CallMethod(enumerableX, "GetEnumerator"); il.PopLocal(enumVarY); if (IsDictionary) { il.DeclareLocal(dictEnumVarX, typeof(IDictionaryEnumerator)); il.CopyLocal(enumVarX, dictEnumVarX); il.DeclareLocal(dictEnumVarY, typeof(IDictionaryEnumerator)); il.CopyLocal(enumVarY, dictEnumVarY); } il.MarkLabel(loopStart); il.CallMethod(enumVarX, "MoveNext"); il.GotoIfFalse(loopEnd); il.CallMethod(enumVarY, "MoveNext"); il.GotoIfFalse(endFalseLabel); // y has less elements than x if (IsDictionary) { il.CallMethod(dictEnumVarX, "get_Key"); il.CallMethod(dictEnumVarY, "get_Key"); GenerateCompareTypeIL(il, this.dictionaryKeyType); il.GotoIfFalse(endFalseLabel); il.CallMethod(dictEnumVarX, "get_Value"); il.CallMethod(dictEnumVarY, "get_Value"); GenerateCompareTypeIL(il, this.dictionaryValueType); il.GotoIfFalse(endFalseLabel); } else // if (not dictionary) { il.CallMethod(enumVarX, "get_Current"); il.CallMethod(enumVarY, "get_Current"); GenerateCompareTypeIL(il, this.elementType); il.GotoIfFalse(endFalseLabel); } il.Goto(loopStart); il.MarkLabel(loopEnd); // enumVarX has no more elements so enumVarY shouldn't // have any more either il.CallMethod(enumVarY, "MoveNext"); il.GotoIfTrue(endFalseLabel); // count mismatch } else // if (not collection) { il.PushLocalAsObject(propVarX); il.PushLocalAsObject(propVarY); GenerateCompareTypeIL(il, this.Type); il.GotoIfFalse(endFalseLabel); } il.MarkLabel(endTrueLabel); //il.DebugWriteLine(string.Format("Property {0} is equal", this.Name)); il.PushInt(1); il.Goto(endLabel); // Log out name of property if not equal il.MarkLabel(endFalseLabel); il.DebugWriteLine(string.Format("Property {0} is not equal", this.Name)); il.PushInt(0); // set success to false il.MarkLabel(endLabel); }