public IntPtr BuildReader(Type type, bool ignoreCustomSerialization) { var constantsBuilder = module.DefineType(type.Name + "_GroBufReader_" + Guid.NewGuid(), TypeAttributes.Class | TypeAttributes.Public); constantsBuilder.DefineField("pointers", typeof(IntPtr[]), FieldAttributes.Private | FieldAttributes.Static); constantsBuilder.DefineField("delegates", typeof(Delegate[]), FieldAttributes.Private | FieldAttributes.Static); var constantsBuilderContext = new ReaderConstantsBuilderContext(groBufReader, constantsBuilder, readerCollection, dataMembersExtractor); constantsBuilderContext.BuildConstants(type, true, ignoreCustomSerialization); var constantsType = constantsBuilder.CreateTypeInfo(); var fields = constantsBuilderContext.GetFields().ToDictionary(pair => pair.Key, pair => pair.Value.Select(constantsType.GetField).ToArray()); var context = new ReaderTypeBuilderContext(groBufReader, module, constantsType, fields, readerCollection, dataMembersExtractor); var reader = context.GetReader(type, true, ignoreCustomSerialization); var initializer = BuildInitializer(constantsType.GetField("pointers", BindingFlags.Static | BindingFlags.NonPublic), constantsType.GetField("delegates", BindingFlags.Static | BindingFlags.NonPublic)); var compiledDynamicMethods = context.GetMethods(); var pointers = new IntPtr[compiledDynamicMethods.Length]; var delegates = new Delegate[compiledDynamicMethods.Length]; foreach (var pair in compiledDynamicMethods) { var compiledDynamicMethod = pair.Value; var index = compiledDynamicMethod.Index; pointers[index] = compiledDynamicMethod.Pointer; delegates[index] = compiledDynamicMethod.Delegate; if (compiledDynamicMethod.Pointer != reader.Pointer) { groBufReader.readMethodsWithCustomSerialization[pair.Key] = (IntPtr?)compiledDynamicMethod.Pointer; } } initializer(pointers, delegates, context.GetFieldInitializers()); return(reader.Pointer); }
public ReaderMethodBuilderContext(ReaderTypeBuilderContext context, GroboIL il, bool referenceType) { Context = context; Il = il; TypeCode = il.DeclareLocal(typeof(int)); Length = il.DeclareLocal(typeof(uint)); Index = referenceType ? il.DeclareLocal(typeof(int)) : null; }
public static void LoadReader(GroboIL il, Type type, ReaderTypeBuilderContext context) { var counter = context.GetReader(type); if (counter.Pointer != IntPtr.Zero) { il.Ldc_IntPtr(counter.Pointer); } else { il.Ldfld(context.ConstantsType.GetField("pointers", BindingFlags.Static | BindingFlags.NonPublic)); il.Ldc_I4(counter.Index); il.Ldelem(typeof(IntPtr)); } }
private void BuildMembersTable(ReaderTypeBuilderContext context, out ulong[] hashCodes, out MemberInfo[] dataMembers) { var members = context.GetDataMembers(Type); var hashes = GroBufHelpers.CalcHashesAndCheck(members); var n = GroBufHelpers.CalcSize(hashes); hashCodes = new ulong[n]; dataMembers = new MemberInfo[n]; for (var i = 0; i < members.Length; i++) { var index = (int)(hashes[i] % n); hashCodes[index] = hashes[i]; dataMembers[index] = members[i].Member; } }
public static void CallReader(GroboIL il, Type type, ReaderTypeBuilderContext context) { LoadReader(il, type, context); il.Calli(CallingConventions.Standard, typeof(void), new[] { typeof(IntPtr), typeof(int).MakeByRefType(), type.MakeByRefType(), typeof(ReaderContext) }); }
private KeyValuePair <Delegate, IntPtr> GetMemberSetter(ReaderTypeBuilderContext context, MemberInfo member) { var method = new DynamicMethod("Set_" + Type.Name + "_" + member.Name + "_" + Guid.NewGuid(), typeof(void), new[] { typeof(IntPtr), typeof(int).MakeByRefType(), Type.MakeByRefType(), typeof(ReaderContext) }, context.Module, true); using (var il = new GroboIL(method)) { il.Ldarg(0); // stack: [data] il.Ldarg(1); // stack: [data, ref index] switch (member.MemberType) { case MemberTypes.Field: var field = (FieldInfo)member; var done = false; if (member.GetCustomAttributes(typeof(IgnoreDefaultOnMergeAttribute), false).Length > 0 && field.FieldType.IsValueType) { var equalityOperator = field.FieldType.GetMethod("op_Equality", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); if (field.FieldType.IsPrimitive || equalityOperator != null) { var fieldValue = il.DeclareLocal(field.FieldType); il.Ldarg(2); // stack: [data, ref index, ref result] if (!Type.IsValueType) { il.Ldind(Type); // stack: [data, ref index, result] } il.Ldfld(field); il.Stloc(fieldValue); il.Ldloca(fieldValue); il.Ldarg(3); // stack: [data, ref index, ref result.field, context] ReaderMethodBuilderContext.CallReader(il, field.FieldType, context); // reader(data, ref index, ref result.field, context); stack: [] var temp = il.DeclareLocal(field.FieldType); il.Ldloca(temp); il.Initobj(field.FieldType); il.Ldloc(temp); il.Ldloc(fieldValue); if (field.FieldType.IsPrimitive) { il.Ceq(); } else { il.Call(equalityOperator); } var notDefaultLabel = il.DefineLabel("notDefault"); il.Brfalse(notDefaultLabel); il.Ret(); il.MarkLabel(notDefaultLabel); il.Ldarg(2); if (!Type.IsValueType) { il.Ldind(Type); // stack: [data, ref index, result] } il.Ldloc(fieldValue); il.Stfld(field); done = true; } } if (!done) { il.Ldarg(2); // stack: [data, ref index, ref result] if (!Type.IsValueType) { il.Ldind(Type); // stack: [data, ref index, result] } il.Ldflda(field); // stack: [data, ref index, ref result.field] il.Ldarg(3); // stack: [data, ref index, ref result.field, context] ReaderMethodBuilderContext.CallReader(il, field.FieldType, context); // reader(data, ref index, ref result.field, context); stack: [] } break; case MemberTypes.Property: var property = (PropertyInfo)member; var propertyValue = il.DeclareLocal(property.PropertyType); if (context.GroBufReader.Options.HasFlag(GroBufOptions.MergeOnRead)) { var getter = property.GetGetMethod(true); if (getter == null) { throw new MissingMethodException(Type.Name, property.Name + "_get"); } il.Ldarg(2); // stack: [data, ref index, ref result] if (!Type.IsValueType) { il.Ldind(Type); // stack: [data, ref index, result] } il.Call(getter, Type); // stack: [ data, ref index, result.property] il.Stloc(propertyValue); // propertyValue = result.property; stack: [data, ref index] } il.Ldloca(propertyValue); // stack: [data, ref index, ref propertyValue] il.Ldarg(3); // stack: [data, ref index, ref propertyValue, context] ReaderMethodBuilderContext.CallReader(il, property.PropertyType, context); // reader(data, ref index, ref propertyValue, context); stack: [] if (member.GetCustomAttributes(typeof(IgnoreDefaultOnMergeAttribute), false).Length > 0 && property.PropertyType.IsValueType) { var equalityOperator = property.PropertyType.GetMethod("op_Equality", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); if (property.PropertyType.IsPrimitive || equalityOperator != null) { var temp = il.DeclareLocal(property.PropertyType); il.Ldloca(temp); il.Initobj(property.PropertyType); il.Ldloc(temp); il.Ldloc(propertyValue); if (property.PropertyType.IsPrimitive) { il.Ceq(); } else { il.Call(equalityOperator); } var notDefaultLabel = il.DefineLabel("notDefault"); il.Brfalse(notDefaultLabel); il.Ret(); il.MarkLabel(notDefaultLabel); } } il.Ldarg(2); // stack: [ref result] if (!Type.IsValueType) { il.Ldind(Type); // stack: [result] } il.Ldloc(propertyValue); // stack: [result, propertyValue] var setter = property.GetSetMethod(true); if (setter == null) { throw new MissingMethodException(Type.Name, property.Name + "_set"); } il.Call(setter, Type); // result.property = propertyValue break; default: throw new NotSupportedException("Data member of type '" + member.MemberType + "' is not supported"); } il.Ret(); } var @delegate = method.CreateDelegate(typeof(ReaderDelegate <>).MakeGenericType(Type)); return(new KeyValuePair <Delegate, IntPtr>(@delegate, GroBufHelpers.ExtractDynamicMethodPointer(method))); }
public void BuildReader(ReaderTypeBuilderContext readerTypeBuilderContext) { var method = new DynamicMethod("Read_" + Type.Name + "_" + Guid.NewGuid(), typeof(void), new[] { typeof(IntPtr), typeof(int).MakeByRefType(), Type.MakeByRefType(), typeof(ReaderContext) }, readerTypeBuilderContext.Module, true); readerTypeBuilderContext.SetReaderMethod(Type, method); using (var il = new GroboIL(method)) { var context = new ReaderMethodBuilderContext(readerTypeBuilderContext, il, !Type.IsValueType && IsReference); ReadTypeCodeAndCheck(context); // Read TypeCode and check if (!Type.IsValueType && IsReference) { // Read reference context.LoadContext(); // stack: [context] il.Ldfld(ReaderContext.ObjectsField); // stack: [context.objects] var notReadLabel = il.DefineLabel("notRead"); il.Brfalse(notReadLabel); context.LoadIndex(); // stack: [external index] context.LoadContext(); // stack: [external index, context] il.Ldfld(ReaderContext.StartField); // stack: [external index, context.start] il.Sub(); // stack: [external index - context.start] il.Stloc(context.Index); // index = external index - context.start; stack: [] context.LoadContext(); // stack: [context] il.Ldfld(ReaderContext.ObjectsField); // stack: [context.objects] il.Ldloc(context.Index); // stack: [context.objects, index] var obj = il.DeclareLocal(typeof(object)); il.Ldloca(obj); object dummy; il.Call(HackHelpers.GetMethodDefinition <Dictionary <int, object> >(dict => dict.TryGetValue(0, out dummy))); // stack: [context.objects.TryGetValue(index, out obj)] il.Brfalse(notReadLabel); // if(!context.objects.TryGetValue(index, out obj)) goto notRead; context.LoadResultByRef(); // stack: [ref result] il.Ldloc(obj); // stack: [ref result, obj] il.Castclass(Type); // stack: [ref result, (Type)obj] il.Stind(Type); // result = (Type)obj; stack: [] context.IncreaseIndexBy1(); // Skip type code context.SkipValue(); // Skip value - it has already been read il.Ret(); il.MarkLabel(notReadLabel); il.Ldloc(context.TypeCode); // stack: [typeCode] il.Ldc_I4((int)GroBufTypeCode.Reference); // stack: [typeCode, GroBufTypeCode.Reference] var readUsualLabel = il.DefineLabel("readUsual"); il.Bne_Un(readUsualLabel); // if(typeCode != GroBufTypeCode.Reference) goto readUsual; stack: [] context.LoadContext(); // stack: [context] il.Ldfld(ReaderContext.ObjectsField); // stack: [context.objects] var objectsIsNotNullLabel = il.DefineLabel("objectsIsNotNull"); il.Brtrue(objectsIsNotNullLabel); // if(context.objects != null) goto objectsIsNotNull; stack: [context.objects] il.Ldstr("Reference is not valid at this point"); il.Newobj(typeof(DataCorruptedException).GetConstructor(new[] { typeof(string) })); il.Throw(); il.MarkLabel(objectsIsNotNullLabel); context.IncreaseIndexBy1(); // index = index + 1; stack: [] il.Ldc_I4(4); context.AssertLength(); context.GoToCurrentLocation(); var reference = il.DeclareLocal(typeof(int)); il.Ldind(typeof(int)); // stack: [*(int*)data[index]] il.Stloc(reference); // reference = *(int*)data[index]; stack: [] context.IncreaseIndexBy4(); // index = index + 4; stack: [] il.Ldloc(context.Index); // stack: [index] il.Ldloc(reference); // stack: [index, reference] var goodReferenceLabel = il.DefineLabel("goodReference"); il.Bgt(goodReferenceLabel, false); // if(index > reference) goto goodReference; stack: [] il.Ldstr("Bad reference"); il.Newobj(typeof(DataCorruptedException).GetConstructor(new[] { typeof(string) })); il.Throw(); il.MarkLabel(goodReferenceLabel); context.LoadContext(); // stack: [context] il.Ldfld(ReaderContext.ObjectsField); // stack: [context.objects] il.Ldloc(reference); // stack: [context.objects, reference] il.Ldloca(obj); // stack: [context.objects, reference, ref obj] il.Call(HackHelpers.GetMethodDefinition <Dictionary <int, object> >(dict => dict.TryGetValue(0, out dummy))); // stack: [context.objects.TryGetValue(reference, out obj)] var readObjectLabel = il.DefineLabel("readObject"); il.Brfalse(readObjectLabel); // if(!context.objects.TryGetValue(reference, out obj)) goto readObjects; stack: [] context.LoadResultByRef(); // stack: [ref result] il.Ldloc(obj); // stack: [ref result, obj] il.Castclass(Type); // stack: [ref result, (Type)obj] il.Stind(Type); // result = (Type)obj; stack: [] il.Ret(); il.MarkLabel(readObjectLabel); // Referenced object has not been read - this means that the object reference belongs to is a property that had been deleted context.LoadData(); // stack: [data] il.Ldloc(reference); // stack: [data, reference] context.LoadContext(); // stack: [data, reference, context] il.Ldfld(ReaderContext.StartField); // stack: [data, reference, context.start] il.Add(); // stack: [data, reference + context.start] il.Stloc(reference); // reference += context.start; stack: [data] il.Ldloca(reference); // stack: [data, ref reference] context.LoadResultByRef(); // stack: [data, ref reference, ref result] context.LoadContext(); // stack: [data, ref reference, ref result, context] context.CallReader(Type); il.Ret(); il.MarkLabel(readUsualLabel); } ReadNotEmpty(context); // Read obj il.Ret(); } var @delegate = method.CreateDelegate(typeof(ReaderDelegate <>).MakeGenericType(Type)); var pointer = GroBufHelpers.ExtractDynamicMethodPointer(method); readerTypeBuilderContext.SetReaderPointer(Type, pointer, @delegate); }