private static Action <Hashtable, Array, int> EmitCopyValues() { /* * private void CopyValues(Hashtable hashtable, Array array, int arrayLength) * { * bucket[] buckets = hashtable.buckets; * int length = buckets.Length; * int arrayIndex = 0; * while (--length >= 0) * { * object key = buckets[length].key; * if ((key != null) && (key != hashtable.buckets)) * { * if (arrayIndex >= arrayLength) * return; * array.SetValue(buckets[length].val, arrayIndex++); * } * } * } */ var dynamicMethod = new DynamicMethod( Guid.NewGuid().ToString(), typeof(void), new[] { typeof(Hashtable), typeof(Array), typeof(int) }, typeof(Hashtable), true); var bucketsFieldInfo = typeof(Hashtable).GetField("buckets", BindingFlags.Instance | BindingFlags.NonPublic); var bucketType = typeof(Hashtable).GetNestedType("bucket", BindingFlags.NonPublic); var bucketArrayType = bucketType.MakeArrayType(); var keyFieldInfo = bucketType.GetField("key", BindingFlags.Instance | BindingFlags.Public); var valFieldInfo = bucketType.GetField("val", BindingFlags.Instance | BindingFlags.Public); var setValueMethodInfo = typeof(Array).GetMethod("SetValue", new[] { typeof(object), typeof(int) }); using (var il = new GroboIL(dynamicMethod)) { var buckets = il.DeclareLocal(bucketArrayType, "buckets"); var length = il.DeclareLocal(typeof(int), "length"); var arrayIndex = il.DeclareLocal(typeof(int), "arrayIndex"); var key = il.DeclareLocal(typeof(object), "key"); var cycleStartLabel = il.DefineLabel("cycleStart"); var cycleNextLabel = il.DefineLabel("cycleNext"); var endLabel = il.DefineLabel("end"); il.EmitSetIntToZero(arrayIndex); il.Ldarg(0); // stack: hashtable il.Ldfld(bucketsFieldInfo); // stack: hashtable::buckets il.Stloc(buckets); il.Ldloc(buckets); // stack: hashtable::buckets il.Ldlen(); // stack: buckets.length il.Conv <int>(); // stack: buckets.length (i4) il.Stloc(length); il.Br(cycleNextLabel); // jump(cycleNext) il.MarkLabel(cycleStartLabel); il.EmitLoadArrayItemRef(buckets, length, bucketType); // stack: *bucket[current] il.Ldfld(keyFieldInfo); // stack: key il.Stloc(key); // 2: key il.Ldloc(key); // stack: key il.Brfalse(cycleNextLabel); // jump(cycleNext) if key == null il.Ldloc(key); // stack: key il.Ldarg(0); // stack+: hashtable il.Ldfld(bucketsFieldInfo); // stack: key, hashtable::buckets il.Beq(cycleNextLabel); // jump(cycleNext) if key == hashtable::buckets (какой-то хитрый хак hashtable-а) il.Ldloc(arrayIndex); // stack: arrayIndex il.Ldarg(2); // stack+: arrayLength il.Bge(endLabel, false); // jump(end) if arrayIndex >= arrayLength il.Ldarg(1); // stack: array (arg1) il.EmitLoadArrayItemRef(buckets, length, bucketType); // stack: array (arg1), *bucket[current] il.Ldfld(valFieldInfo); // stack: array (arg1), bucket[current].val il.EmitXPlusPlus(arrayIndex); // stack: array (arg1), bucket[current].val, arrayIndex++ il.Call(setValueMethodInfo); // array.SetValue(bucket[current].val, old_arrayIndex); il.MarkLabel(cycleNextLabel); il.EmitMinusMinusX(length); // stack: --current il.Ldc_I4(0); // stack+: 0 il.Bge(cycleStartLabel, false); // jump(cycleStart) if --current >= 0 il.MarkLabel(endLabel); il.Ret(); } return((Action <Hashtable, Array, int>)dynamicMethod.CreateDelegate(typeof(Action <Hashtable, Array, int>))); }
private Type BuildReaderInvoker() { var argument = Type.GetGenericArguments()[0]; var typeBuilder = module.DefineType("ReaderInvoker_" + Type, TypeAttributes.Public | TypeAttributes.Class); var reader = typeBuilder.DefineField("reader", typeof(IntPtr), FieldAttributes.Private); var serializerId = typeBuilder.DefineField("serializerId", typeof(long), FieldAttributes.Private); var constructor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, new[] { typeof(IntPtr), typeof(long) }); using (var il = new GroboIL(constructor)) { il.Ldarg(0); // stack: [this] il.Ldarg(1); // stack: [this, reader] il.Stfld(reader); // this.reader = reader; stack: [] il.Ldarg(0); // stack: [this] il.Ldarg(2); // stack: [this, serializerId] il.Stfld(serializerId); // this.serializerId = serializerId; stack: [] il.Ret(); } var method = typeBuilder.DefineMethod("Read", MethodAttributes.Public, argument, new[] { typeof(byte[]) }); using (var il = new GroboIL(method)) { var pinnedData = il.DeclareLocal(typeof(byte).MakeByRefType(), "pinnedData", true); il.Ldarg(1); // stack: [data] il.Ldc_I4(0); // stack: [data, 0] il.Ldelema(typeof(byte)); // stack: [&data[0]] il.Stloc(pinnedData); // pinnedData = &data[0]; stack: [] var index = il.DeclareLocal(typeof(int), "index"); il.Ldc_I4(0); // stack: [0] il.Stloc(index); // index = 0; stack: [] var context = il.DeclareLocal(typeof(ReaderContext), "context"); il.Ldarg(0); // stack: [this] il.Ldfld(serializerId); // stack: [this.serializerId] il.Ldarg(1); // stack: [this.serializerId, data] il.Ldlen(); // stack: [this.serializerId, data.Length] il.Ldc_I4(0); // stack: [this.serializerId, data.Length, 0] il.Ldc_I4(0); // stack: [this.serializerId, data.Length, 0, 0] il.Newobj(typeof(ReaderContext).GetConstructor(new[] { typeof(long), typeof(int), typeof(int), typeof(int) })); // stack: [new ReaderContext(this.serializerId, data.Length, 0, 0)] il.Stloc(context); // context = new ReaderContext(..); stack: [] var result = il.DeclareLocal(argument, "result"); il.Ldloc(pinnedData); // stack: [data] il.Conv <IntPtr>(); // stack: [(IntPtr)data] il.Ldloca(index); // stack: [(IntPtr)data, ref index] il.Ldloca(result); // stack: [(IntPtr)data, ref index, ref result] il.Ldloc(context); // stack: [(IntPtr)data, ref index, ref result, context] il.Ldarg(0); // stack: [(IntPtr)data, ref index, ref result, context, this] il.Ldfld(reader); // stack: [(IntPtr)data, ref index, ref result, context, this.reader] var parameterTypes = new[] { typeof(IntPtr), typeof(int).MakeByRefType(), argument.MakeByRefType(), typeof(ReaderContext) }; il.Calli(CallingConventions.Standard, typeof(void), parameterTypes); // this.reader((IntPtr)data, ref index, ref result, context); stack: [] il.FreePinnedLocal(pinnedData); // pinnedData = null; stack: [] var retLabel = il.DefineLabel("ret"); il.Ldarg(1); // stack: [data] il.Ldlen(); // stack: [data.Length] il.Ldloc(index); // stack: [data.Length, index] il.Beq(retLabel); // if(data.Length == index) goto ret; stack: [] il.Ldstr("Encountered extra data"); il.Newobj(typeof(DataCorruptedException).GetConstructor(new[] { typeof(string) })); il.Throw(); il.MarkLabel(retLabel); il.Ldloc(result); il.Ret(); } return(typeBuilder.CreateType()); }
public void BuildWriter(WriterTypeBuilderContext writerTypeBuilderContext) { var method = new DynamicMethod("Write_" + Type.Name + "_" + Guid.NewGuid(), typeof(void), new[] { Type, typeof(bool), typeof(IntPtr), typeof(int).MakeByRefType(), typeof(WriterContext) }, writerTypeBuilderContext.Module, true); writerTypeBuilderContext.SetWriterMethod(Type, method); using (var il = new GroboIL(method)) { var context = new WriterMethodBuilderContext(writerTypeBuilderContext, il); var notEmptyLabel = il.DefineLabel("notEmpty"); if (CheckEmpty(context, notEmptyLabel)) // Check if obj is empty { context.WriteNull(); // Write null & return } il.MarkLabel(notEmptyLabel); // Now we know that obj is not empty if (!Type.IsValueType && IsReference && writerTypeBuilderContext.GroBufWriter.Options.HasFlag(GroBufOptions.PackReferences)) { // Pack reference var index = il.DeclareLocal(typeof(int)); context.LoadIndex(); // stack: [external index] context.LoadContext(); // stack: [external index, context] il.Ldfld(WriterContext.StartField); // stack: [external index, context.start] il.Sub(); // stack: [external index - context.start] il.Stloc(index); // index = external index - context.start; stack: [] context.LoadContext(); // stack: [context] il.Ldfld(typeof(WriterContext).GetField("objects", BindingFlags.Public | BindingFlags.Instance)); // stack: [context.objects] context.LoadObj(); // stack: [context.objects, obj] var reference = il.DeclareLocal(typeof(int)); il.Ldloca(reference); // stack: [context.objects, obj, ref reference] int dummy; il.Call(HackHelpers.GetMethodDefinition <Dictionary <object, int> >(dict => dict.TryGetValue(null, out dummy))); // stack: [context.object.TryGetValue(obj, out reference)] var storeLocationLabel = il.DefineLabel("storeLocation"); il.Brfalse(storeLocationLabel); // Current object is in dict il.Ldloc(index); il.Ldloc(reference); // stack: [index, reference] var skipSelfLabel = il.DefineLabel("skipSelf"); il.Beq(skipSelfLabel); // if(index == reference) goto skipSelf; stack: [] il.Ldloc(index); // stack: [index] il.Ldloc(reference); // stack: [index, reference] var badReferenceLabel = il.DefineLabel("badReference"); il.Blt(badReferenceLabel, false); // if(index < reference) goto badReference; stack: [] context.WriteTypeCode(GroBufTypeCode.Reference); // result[index++] = GroBufTypeCode.Reference context.GoToCurrentLocation(); // stack: [&result[index]] il.Ldloc(reference); // stack: [&result[index], reference] il.Stind(typeof(int)); // *(int *)&result[index] = reference context.IncreaseIndexBy4(); // index += 4 il.Ret(); il.MarkLabel(badReferenceLabel); il.Ldstr("Bad reference"); il.Newobj(typeof(DataCorruptedException).GetConstructor(new[] { typeof(string) })); il.Throw(); il.MarkLabel(storeLocationLabel); context.LoadContext(); // stack: [context] il.Ldfld(typeof(WriterContext).GetField("objects", BindingFlags.Public | BindingFlags.Instance)); // stack: [context.objects] context.LoadObj(); // stack: [context.objects, obj] il.Ldloc(index); // stack: [context.objects, obj, index] il.Call(HackHelpers.GetMethodDefinition <Dictionary <object, int> >(dict => dict.Add(null, 0))); // context.objects.Add(obj, index); il.MarkLabel(skipSelfLabel); } WriteNotEmpty(context); // Write obj il.Ret(); } var @delegate = method.CreateDelegate(typeof(WriterDelegate <>).MakeGenericType(Type)); var pointer = GroBufHelpers.ExtractDynamicMethodPointer(method); writerTypeBuilderContext.SetWriterPointer(Type, pointer, @delegate); }