private static void GenerateReadMethod(ReaderGenerationContext context) { // method returns read methodInfo (or null) context.PushObjectReaderOntoStack(); context.Generator.Emit(OpCodes.Call, Helpers.GetPropertyGetterInfo <ObjectReader, IdentifiedElementsList <MethodDescriptor> >(or => or.Methods)); context.Generator.Emit(OpCodes.Call, Helpers.GetMethodInfo <IdentifiedElementsList <MethodDescriptor>, MethodDescriptor>(or => or.Read())); context.Generator.Emit(OpCodes.Call, Helpers.GetPropertyGetterInfo <MethodDescriptor, MethodInfo>(md => md.UnderlyingMethod)); }
private void GenerateDesurrogate(ReaderGenerationContext context, int factoryId) { if (factoryId == -1) { return; } var desurrogatedObjectLocal = context.Generator.DeclareLocal(typeof(object)); // obtain surrogate factory context.PushObjectReaderOntoStack(); context.Generator.PushFieldValueOntoStack <ObjectReader, SwapList>(x => x.objectsForSurrogates); context.Generator.PushIntegerOntoStack(factoryId); context.Generator.Call <SwapList>(x => x.GetByIndex(0)); // recreate an object from the surrogate var delegateType = typeof(Func <,>).MakeGenericType(type, typeof(object)); context.Generator.Emit(OpCodes.Castclass, delegateType); context.PushDeserializedObjectOntoStack(context.PushObjectIdOntoStack); context.Generator.Emit(OpCodes.Call, delegateType.GetMethod("Invoke")); context.Generator.StoreLocalValueFromStack(desurrogatedObjectLocal); // clone context context.PushObjectReaderOntoStack(); context.Generator.PushFieldValueOntoStack <ObjectReader, Serializer.ReadMethods>(x => x.readMethods); context.Generator.PushFieldValueOntoStack <Serializer.ReadMethods, DynamicMethodProvider <CloneMethodDelegate> >(x => x.cloneContentMehtodsProvider); context.Generator.PushLocalValueOntoStack(desurrogatedObjectLocal); context.Generator.Call <object>(x => x.GetType()); context.Generator.Call <DynamicMethodProvider <CloneMethodDelegate> >(x => x.GetOrCreate(typeof(void))); context.Generator.PushLocalValueOntoStack(desurrogatedObjectLocal); context.PushDeserializedObjectOntoStack(context.PushObjectIdOntoStack, true); context.Generator.Call <CloneMethodDelegate>(x => x.Invoke(null, null)); //remove object reference from surrogatesWhileReading collection context.PushObjectReaderOntoStack(); context.Generator.PushFieldValueOntoStack <ObjectReader, OneToOneMap <int, object> >(x => x.surrogatesWhileReading); context.PushObjectIdOntoStack(); context.Generator.Call <OneToOneMap <int, object> >(x => x.Remove(0)); }
private static void GenerateTouchObject(ReaderGenerationContext context, Type formalType) { var finish = context.Generator.DefineLabel(); context.PushObjectIdOntoStack(); context.PushDeserializedObjectsCollectionOntoStack(); context.Generator.PushPropertyValueOntoStack <AutoResizingList <object>, int>(x => x.Count); // if (refId < deserializedObjects.Count) return; context.Generator.Emit(OpCodes.Blt, finish); if (CreateObjectGenerator.TryFillBody(context.Generator, formalType, context.TreatCollectionAsUserObject, x => { context.PushObjectReaderOntoStack(); context.PushObjectIdOntoStack(); })) { context.Generator.Call <ObjectReader>(x => x.SetObjectByReferenceId(0, null)); } context.Generator.MarkLabel(finish); }
private static void GenerateReadField(ReaderGenerationContext context, Type formalType, bool boxIfValueType = true) { // method returns read field value on stack if (Helpers.IsTransient(formalType)) { context.Generator.PushTypeOntoStack(formalType); context.Generator.Emit(OpCodes.Call, Helpers.GetMethodInfo <object, object>(x => Helpers.GetDefaultValue(null))); if (formalType.IsValueType && boxIfValueType) { context.Generator.Emit(OpCodes.Box, formalType); } return; //return Helpers.GetDefaultValue(_formalType); } var finishLabel = context.Generator.DefineLabel(); if (!formalType.IsValueType) { var referenceIdLocal = context.Generator.DeclareLocal(typeof(int)); var returnDeserializeObjectLabel = context.Generator.DefineLabel(); context.PushObjectReaderOntoStack(); context.Generator.Call <ObjectReader>(x => x.ReadAndTouchReference()); context.Generator.Emit(OpCodes.Dup); context.Generator.StoreLocalValueFromStack(referenceIdLocal); context.Generator.Emit(OpCodes.Ldc_I4, Consts.NullObjectId); context.Generator.Emit(OpCodes.Bne_Un, returnDeserializeObjectLabel); context.Generator.Emit(OpCodes.Ldnull); context.Generator.Emit(OpCodes.Br, finishLabel); context.Generator.MarkLabel(returnDeserializeObjectLabel); context.PushDeserializedObjectOntoStack(referenceIdLocal, true); context.Generator.Emit(OpCodes.Castclass, formalType); context.Generator.MarkLabel(finishLabel); return; } var continueWithNullableLabel = context.Generator.DefineLabel(); var forcedFormalType = formalType; var forcedBoxIfValueType = boxIfValueType; var nullableActualType = Nullable.GetUnderlyingType(formalType); if (nullableActualType != null) { forcedFormalType = nullableActualType; forcedBoxIfValueType = true; GenerateReadPrimitive(context, typeof(bool)); context.Generator.Emit(OpCodes.Brtrue, continueWithNullableLabel); context.Generator.Emit(OpCodes.Ldnull); context.Generator.Emit(OpCodes.Br, finishLabel); context.Generator.MarkLabel(continueWithNullableLabel); } if (forcedFormalType.IsEnum) { var actualType = Enum.GetUnderlyingType(forcedFormalType); context.Generator.PushTypeOntoStack(forcedFormalType); GenerateReadPrimitive(context, actualType); context.Generator.Emit(OpCodes.Call, typeof(Enum).GetMethod("ToObject", BindingFlags.Static | BindingFlags.Public, null, new[] { typeof(Type), actualType }, null)); if (!forcedBoxIfValueType) { context.Generator.Emit(OpCodes.Unbox_Any, forcedFormalType); } } else if (Helpers.IsWriteableByPrimitiveWriter(forcedFormalType)) { // value type GenerateReadPrimitive(context, forcedFormalType); if (forcedBoxIfValueType) { context.Generator.Emit(OpCodes.Box, forcedFormalType); } } else { // here we have struct var structLocal = context.Generator.DeclareLocal(forcedFormalType); GenerateUpdateStructFields(context, forcedFormalType, structLocal); context.Generator.PushLocalValueOntoStack(structLocal); if (forcedBoxIfValueType) { context.Generator.Emit(OpCodes.Box, forcedFormalType); } } context.Generator.MarkLabel(finishLabel); // if the value is nullable we must use special initialization of it if (nullableActualType != null) { var nullableLocal = context.Generator.DeclareLocal(formalType); var returnNullNullableLabel = context.Generator.DefineLabel(); var endLabel = context.Generator.DefineLabel(); context.Generator.Emit(OpCodes.Dup); context.Generator.Emit(OpCodes.Brfalse, returnNullNullableLabel); if (forcedBoxIfValueType) { context.Generator.Emit(OpCodes.Unbox_Any, nullableActualType); } context.Generator.Emit(OpCodes.Newobj, formalType.GetConstructor(new[] { nullableActualType })); context.Generator.StoreLocalValueFromStack(nullableLocal); context.Generator.PushLocalValueOntoStack(nullableLocal); if (boxIfValueType) { context.Generator.Emit(OpCodes.Box, formalType); } context.Generator.Emit(OpCodes.Br, endLabel); context.Generator.MarkLabel(returnNullNullableLabel); context.Generator.Emit(OpCodes.Pop); context.Generator.PushLocalAddressOntoStack(nullableLocal); context.Generator.Emit(OpCodes.Initobj, formalType); context.Generator.PushLocalValueOntoStack(nullableLocal); if (boxIfValueType) { context.Generator.Emit(OpCodes.Box, nullableLocal); } context.Generator.MarkLabel(endLabel); } }
private static void GenerateFillCollection(ReaderGenerationContext context, CollectionMetaToken collectionToken, Type collectionType, LocalBuilder objectIdLocal) { var countLocal = context.Generator.DeclareLocal(typeof(int)); GenerateReadPrimitive(context, typeof(int)); context.Generator.StoreLocalValueFromStack(countLocal); // read collection elements count var addMethod = collectionToken.AddMethod; var elementFormalType = collectionToken.FormalElementType; if (collectionType == typeof(Stack) || (collectionType.IsGenericType && collectionType.GetGenericTypeDefinition() == typeof(Stack <>))) { var tempArrLocal = context.Generator.DeclareLocal(elementFormalType.MakeArrayType()); context.Generator.PushLocalValueOntoStack(countLocal); context.Generator.Emit(OpCodes.Newarr, elementFormalType); context.Generator.StoreLocalValueFromStack(tempArrLocal); // creates temporal array GeneratorHelper.GenerateLoop(context, countLocal, cl => { context.Generator.PushLocalValueOntoStack(tempArrLocal); context.Generator.PushLocalValueOntoStack(cl); GenerateReadField(context, elementFormalType, false); context.Generator.Emit(OpCodes.Stelem, elementFormalType); }); GeneratorHelper.GenerateLoop(context, countLocal, cl => { context.PushDeserializedObjectOntoStack(objectIdLocal); context.Generator.Emit(OpCodes.Castclass, collectionType); context.Generator.PushLocalValueOntoStack(tempArrLocal); context.Generator.PushLocalValueOntoStack(cl); context.Generator.Emit(OpCodes.Ldelem, elementFormalType); context.Generator.Emit(OpCodes.Callvirt, addMethod); }, true); } else { GeneratorHelper.GenerateLoop(context, countLocal, cl => { context.PushDeserializedObjectOntoStack(objectIdLocal); context.Generator.Emit(OpCodes.Castclass, collectionType); var currentIdLocal = default(LocalBuilder); var debtDifference = default(LocalBuilder); var valueLocal = default(LocalBuilder); if (collectionToken.IsHashCodeBased) { currentIdLocal = context.Generator.DeclareLocal(typeof(int)); debtDifference = context.Generator.DeclareLocal(typeof(int)); } if (collectionToken.IsDictionary) { valueLocal = context.Generator.DeclareLocal(collectionToken.FormalValueType); } if (collectionToken.IsHashCodeBased) { context.PushObjectReaderOntoStack(); context.Generator.Call <ObjectReader>(or => or.GetNewestObjectId()); context.Generator.StoreLocalValueFromStack(currentIdLocal); context.PushObjectReaderOntoStack(); context.Generator.Call <ObjectReader>(or => or.GetObjectDebt()); context.Generator.StoreLocalValueFromStack(debtDifference); } GenerateReadField(context, collectionToken.IsDictionary ? collectionToken.FormalKeyType : collectionToken.FormalElementType, false); if (collectionToken.IsHashCodeBased) { context.PushObjectReaderOntoStack(); context.Generator.Call <ObjectReader>(or => or.GetObjectDebt()); // Here we have new debt on top of the stack but need to load old one. context.Generator.PushLocalValueOntoStack(debtDifference); context.Generator.Emit(OpCodes.Sub); context.Generator.StoreLocalValueFromStack(debtDifference); } if (collectionToken.IsDictionary) { GenerateReadField(context, collectionToken.FormalValueType, false); } if (collectionToken.IsHashCodeBased) { var deferredAddLabel = context.Generator.DefineLabel(); var exitLabel = context.Generator.DefineLabel(); context.Generator.PushLocalValueOntoStack(debtDifference); context.Generator.Emit(OpCodes.Brtrue, deferredAddLabel); context.Generator.Emit(OpCodes.Callvirt, addMethod); context.Generator.Emit(OpCodes.Br, exitLabel); context.Generator.MarkLabel(deferredAddLabel); if (collectionToken.IsDictionary) { context.Generator.StoreLocalValueFromStack(valueLocal); } context.Generator.Emit(OpCodes.Pop); // Key (or value if not dictionary) context.Generator.Emit(OpCodes.Pop); // Collection (object) context.PushObjectReaderOntoStack(); context.PushDeserializedObjectOntoStack(objectIdLocal); context.Generator.PushLocalValueOntoStack(currentIdLocal); context.Generator.Emit(OpCodes.Ldc_I4_1); context.Generator.Emit(OpCodes.Add); if (collectionToken.IsDictionary) { context.Generator.PushLocalValueOntoStack(valueLocal); } else { context.Generator.Emit(OpCodes.Ldnull); } context.Generator.Call <ObjectReader>(or => or.AddHashCodeBasedWaitingValue(null, 0, null)); context.Generator.MarkLabel(exitLabel); } else { context.Generator.Emit(OpCodes.Callvirt, addMethod); } if (addMethod.ReturnType != typeof(void)) { context.Generator.Emit(OpCodes.Pop); // remove returned unused value from stack } }); } }
private void GenerateCallPostDeserializationHooks(ReaderGenerationContext context, int factoryId) { var methods = Helpers.GetMethodsWithAttribute(typeof(PostDeserializationAttribute), type).ToArray(); foreach (var method in methods) { if (!method.IsStatic) { context.PushDeserializedObjectOntoStack(context.PushObjectIdOntoStack); context.Generator.Emit(OpCodes.Castclass, method.ReflectedType); } if (method.IsVirtual) { context.Generator.Emit(OpCodes.Callvirt, method); } else { context.Generator.Emit(OpCodes.Call, method); } } methods = Helpers.GetMethodsWithAttribute(typeof(LatePostDeserializationAttribute), type).ToArray(); if (factoryId != -1 && methods.Length != 0) { throw new InvalidOperationException( string.Format(ObjectReader.LateHookAndSurrogateError, type)); } foreach (var method in methods) { context.PushObjectReaderOntoStack(); context.Generator.PushFieldValueOntoStack <ObjectReader, List <Action> >(x => x.latePostDeserializationHooks); context.Generator.PushTypeOntoStack(typeof(Action)); if (method.IsStatic) { context.Generator.Emit(OpCodes.Ldnull); } else { context.PushDeserializedObjectOntoStack(context.PushObjectIdOntoStack); context.Generator.Emit(OpCodes.Castclass, method.ReflectedType); } context.Generator.Emit(OpCodes.Ldtoken, method); if (method.DeclaringType.IsGenericType) { context.Generator.Emit(OpCodes.Ldtoken, method.DeclaringType); context.Generator.Emit(OpCodes.Call, Helpers.GetMethodInfo <object, MethodBase>(x => MethodBase.GetMethodFromHandle(new RuntimeMethodHandle(), new RuntimeTypeHandle()))); } else { context.Generator.Emit(OpCodes.Call, Helpers.GetMethodInfo <object, MethodBase>(x => MethodBase.GetMethodFromHandle(new RuntimeMethodHandle()))); } context.Generator.Emit(OpCodes.Castclass, typeof(MethodInfo)); context.Generator.Emit(OpCodes.Call, Helpers.GetMethodInfo <object, Delegate>(x => Delegate.CreateDelegate(null, null, method))); context.Generator.Emit(OpCodes.Castclass, typeof(Action)); context.Generator.Emit(OpCodes.Call, Helpers.GetMethodInfo <List <Action> >(x => x.Add(null))); } if (callPostDeserializationCallback) { context.PushObjectReaderOntoStack(); context.Generator.PushFieldValueOntoStack <ObjectReader, Action <object> >(x => x.postDeserializationCallback); context.PushDeserializedObjectOntoStack(context.PushObjectIdOntoStack); context.Generator.Emit(OpCodes.Call, Helpers.GetMethodInfo <Action <object> >(x => x.Invoke(null))); } }
protected override void InnerGenerate(ReaderGenerationContext context) { var finish = context.Generator.DefineLabel(); var createdLocal = context.Generator.DeclareLocal(typeof(object)); context.PushObjectIdOntoStack(); context.PushDeserializedObjectsCollectionOntoStack(); context.Generator.PushPropertyValueOntoStack <AutoResizingList <object>, int>(x => x.Count); context.Generator.Emit(OpCodes.Blt, finish); if (CreateObjectGenerator.TryFillBody(context.Generator, type, treatCollectionAsUserObject)) { context.Generator.StoreLocalValueFromStack(createdLocal); context.PushObjectReaderOntoStack(); context.PushObjectIdOntoStack(); context.Generator.PushLocalValueOntoStack(createdLocal); context.Generator.Call <ObjectReader>(x => x.SetObjectByReferenceId(0, null)); } else if (type.IsArray) { var isMultidimensional = type.GetArrayRank() > 1; var elementFormalType = type.GetElementType(); var rankLocal = context.Generator.DeclareLocal(typeof(int)); var lengthsLocal = isMultidimensional ? context.Generator.DeclareLocal(typeof(int[])) : context.Generator.DeclareLocal(typeof(int)); context.PushObjectReaderOntoStack(); context.PushObjectIdOntoStack(); ReadMethodGenerator.GenerateReadPrimitive(context, typeof(int)); context.Generator.StoreLocalValueFromStack(rankLocal); if (isMultidimensional) { context.Generator.PushLocalValueOntoStack(rankLocal); context.Generator.Emit(OpCodes.Newarr, typeof(int)); context.Generator.StoreLocalValueFromStack(lengthsLocal); // create an array for keeping the lengths of each dimension GeneratorHelper.GenerateLoop(context, rankLocal, i => { context.Generator.PushLocalValueOntoStack(lengthsLocal); context.Generator.PushLocalValueOntoStack(i); ReadMethodGenerator.GenerateReadPrimitive(context, typeof(int)); context.Generator.Emit(OpCodes.Stelem, typeof(int)); // populate the lengths with values read from stream }); } else { ReadMethodGenerator.GenerateReadPrimitive(context, typeof(int)); context.Generator.StoreLocalValueFromStack(lengthsLocal); } context.Generator.PushTypeOntoStack(elementFormalType); context.Generator.PushLocalValueOntoStack(lengthsLocal); if (isMultidimensional) { context.Generator.Call(() => Array.CreateInstance(null, new int[0])); } else { context.Generator.Call(() => Array.CreateInstance(null, 0)); } context.Generator.Call <ObjectReader>(x => x.SetObjectByReferenceId(0, null)); } else if (type == typeof(string)) { context.PushObjectReaderOntoStack(); context.PushObjectIdOntoStack(); ReadMethodGenerator.GenerateReadPrimitive(context, typeof(string)); context.Generator.Call <ObjectReader>(x => x.SetObjectByReferenceId(0, null)); var field = Helpers.GetFieldInfo <ObjectReader, int>(x => x.objectsWrittenInlineCount); context.PushObjectReaderOntoStack(); context.Generator.Emit(OpCodes.Dup); context.Generator.Emit(OpCodes.Ldfld, field); context.Generator.PushIntegerOntoStack(1); context.Generator.Emit(OpCodes.Add); context.Generator.Emit(OpCodes.Stfld, field); } else { ReadMethodGenerator.GenerateReadNotPrecreated(context, type, context.PushObjectIdOntoStack); var field = Helpers.GetFieldInfo <ObjectReader, int>(x => x.objectsWrittenInlineCount); context.PushObjectReaderOntoStack(); context.Generator.Emit(OpCodes.Dup); context.Generator.Emit(OpCodes.Ldfld, field); context.Generator.PushIntegerOntoStack(1); context.Generator.Emit(OpCodes.Add); context.Generator.Emit(OpCodes.Stfld, field); } context.Generator.MarkLabel(finish); }