/// <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(); }
static void GenerateCompareTypeIL(DynamicMethodHelper il, Type t) { MethodInfo method = GetTypeMethod(t, TypeMethodType.Compare); if ((method == null) && (t.IsPrimitive == false)) { il.Pop(); il.Pop(); il.PushInt(1); return; } il.BeginScope(); { const string valX = "x"; const string valY = "y"; const string valThis = "_thisX"; const string trueLabel = "endTrue"; const string falseLabel = "endFalse"; const string endLabel = "endCheck"; il.DeclareLocal(valX, typeof(object)); il.PopLocal(valX); il.DeclareLocal(valY, typeof(object)); il.PopLocal(valY); // If both null or the same object then return true il.PushLocal(valX); il.PushLocal(valY); il.GotoIfEqual(trueLabel); // Exit false if either value is null il.PushLocal(valX); il.GotoIfFalse(falseLabel); il.PushLocal(valY); il.GotoIfFalse(falseLabel); // Both operands are non-null so call the type-specific comparison method if (t.IsPrimitive) { il.PushLocal(valX); il.UnboxValueType(t); il.PushLocal(valX); il.UnboxValueType(t); il.CompareEqual(); } else { Type paramType = method.GetParameters()[0].ParameterType; il.DebugWriteLine("Calling " + method); if (method.IsStatic) { il.PushLocal(valX); if (paramType.IsValueType) il.UnboxValueType(paramType); } else { il.DeclareLocal(valThis, t); il.PushLocal(valX); il.PopLocalFromObject(valThis); il.PushThis(valThis, method); } il.PushLocal(valY); if (paramType.IsValueType) il.UnboxValueType(paramType); il.CallMethod(method); } il.GotoIfFalse(falseLabel); il.MarkLabel(trueLabel); il.PushInt(1); il.Goto(endLabel); il.MarkLabel(falseLabel); il.DebugWriteLine("The following values are not equal:"); il.DebugWriteLocal(valX); il.DebugWriteLocal(valY); il.PushInt(0); il.MarkLabel(endLabel); } il.EndScope(); }