private static void GenerateUpdateFields(ReaderGenerationContext context, Type formalType, LocalBuilder objectIdLocal) { var fields = context.DisableStamping ? ((TypeSimpleDescriptor)formalType).FieldsToDeserialize : ((TypeFullDescriptor)formalType).FieldsToDeserialize; foreach (var fieldOrType in fields) { if (fieldOrType.Field == null) { GenerateReadField(context, fieldOrType.TypeToOmit, false); context.Generator.Emit(OpCodes.Pop); continue; } var field = fieldOrType.Field; if (field.IsDefined(typeof(TransientAttribute), false)) { if (field.IsDefined(typeof(ConstructorAttribute), false)) { context.Generator.PushFieldInfoOntoStack(field); context.PushDeserializedObjectOntoStack(objectIdLocal); context.Generator.GenerateCodeCall <FieldInfo, object>(GenerateUpdateFieldsStaticHelper); } continue; } context.PushDeserializedObjectOntoStack(objectIdLocal); GenerateReadField(context, field.FieldType, false); context.Generator.Emit(OpCodes.Stfld, field); } }
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 GenerateUpdateElements(ReaderGenerationContext context, Type formalType, LocalBuilder objectIdLocal) { if (typeof(ISpeciallySerializable).IsAssignableFrom(formalType)) { context.PushDeserializedObjectOntoStack(objectIdLocal); context.Generator.Emit(OpCodes.Castclass, typeof(ISpeciallySerializable)); context.PushPrimitiveReaderOntoStack(); context.Generator.GenerateCodeCall <ISpeciallySerializable, PrimitiveReader>(ObjectReader.LoadAndVerifySpeciallySerializableAndVerify); return; } CollectionMetaToken collectionToken; if (!CollectionMetaToken.TryGetCollectionMetaToken(formalType, out collectionToken)) { throw new InvalidOperationException(InternalErrorMessage); } GenerateFillCollection(context, collectionToken, formalType, objectIdLocal); }
private static void GenerateReadArray(ReaderGenerationContext context, Type arrayType, Action pushObjectIdOntoStackAction) { var isMultidimensional = arrayType.GetArrayRank() > 1; var elementFormalType = arrayType.GetElementType(); var rankLocal = context.Generator.DeclareLocal(typeof(int)); var lengthsLocal = isMultidimensional ? context.Generator.DeclareLocal(typeof(int[])) : context.Generator.DeclareLocal(typeof(int)); var arrayLocal = context.Generator.DeclareLocal(typeof(Array)); var positionLocal = isMultidimensional ? context.Generator.DeclareLocal(typeof(int[])) : context.Generator.DeclareLocal(typeof(int)); var loopControlLocal = context.Generator.DeclareLocal(typeof(int)); // type is int not bool to reuse array length directly var loopBeginLabel = context.Generator.DefineLabel(); var loopEndLabel = context.Generator.DefineLabel(); var nonZeroLengthLabel = context.Generator.DefineLabel(); context.PushDeserializedObjectOntoStack(pushObjectIdOntoStackAction); context.Generator.Emit(OpCodes.Castclass, typeof(Array)); context.Generator.Emit(OpCodes.Dup); context.Generator.StoreLocalValueFromStack(arrayLocal); context.Generator.PushPropertyValueOntoStack <Array, int>(x => x.Rank); context.Generator.StoreLocalValueFromStack(rankLocal); if (isMultidimensional) { context.Generator.Emit(OpCodes.Ldc_I4_1); context.Generator.StoreLocalValueFromStack(loopControlLocal); 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); context.Generator.PushLocalValueOntoStack(arrayLocal); context.Generator.PushLocalValueOntoStack(i); context.Generator.Call <Array>(x => x.GetLength(0)); context.Generator.Emit(OpCodes.Dup); context.Generator.Emit(OpCodes.Brtrue, nonZeroLengthLabel); context.Generator.Emit(OpCodes.Ldc_I4_0); context.Generator.StoreLocalValueFromStack(loopControlLocal); context.Generator.MarkLabel(nonZeroLengthLabel); context.Generator.Emit(OpCodes.Stelem, typeof(int)); // populate the lengths with values read from stream }); } else { context.Generator.PushLocalValueOntoStack(arrayLocal); context.Generator.Emit(OpCodes.Ldc_I4_0); context.Generator.Call <Array>(x => x.GetLength(0)); context.Generator.Emit(OpCodes.Dup); context.Generator.StoreLocalValueFromStack(lengthsLocal); context.Generator.StoreLocalValueFromStack(loopControlLocal); } if (isMultidimensional) { context.Generator.PushLocalValueOntoStack(rankLocal); context.Generator.Emit(OpCodes.Newarr, typeof(int)); context.Generator.StoreLocalValueFromStack(positionLocal); // create an array for keeping the current position of each dimension } context.Generator.MarkLabel(loopBeginLabel); context.Generator.PushLocalValueOntoStack(loopControlLocal); context.Generator.Emit(OpCodes.Brfalse, loopEndLabel); context.Generator.PushLocalValueOntoStack(arrayLocal); context.Generator.Emit(OpCodes.Castclass, arrayType); if (isMultidimensional) { GenerateReadField(context, elementFormalType); context.Generator.PushLocalValueOntoStack(positionLocal); context.Generator.Emit(OpCodes.Call, Helpers.GetMethodInfo <Array>(a => a.SetValue(null, new int[0]))); } else { context.Generator.PushLocalValueOntoStack(positionLocal); context.Generator.Emit(OpCodes.Ldelema, elementFormalType); GenerateReadField(context, elementFormalType, false); context.Generator.Emit(OpCodes.Stobj, elementFormalType); } if (isMultidimensional) { context.Generator.PushLocalValueOntoStack(positionLocal); context.Generator.PushLocalValueOntoStack(lengthsLocal); context.Generator.PushLocalValueOntoStack(rankLocal); context.Generator.GenerateCodeFCall <int[], int[], int, bool>(GenerateReadArrayStaticHelper); } else { context.Generator.PushLocalValueOntoStack(positionLocal); context.Generator.Emit(OpCodes.Ldc_I4_1); context.Generator.Emit(OpCodes.Add); context.Generator.Emit(OpCodes.Dup); context.Generator.StoreLocalValueFromStack(positionLocal); context.Generator.PushLocalValueOntoStack(lengthsLocal); context.Generator.Emit(OpCodes.Clt); } context.Generator.StoreLocalValueFromStack(loopControlLocal); context.Generator.Emit(OpCodes.Br, loopBeginLabel); context.Generator.MarkLabel(loopEndLabel); }
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))); } }