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 static bool Emit(Expression zarr, Expression zindex, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) { var arrayType = zarr.Type; var isArray = arrayType.IsArray; if (!isArray && !arrayType.IsList()) { throw new InvalidOperationException("Unable to perform array index operator to type '" + arrayType + "'"); } var itemType = isArray ? arrayType.GetElementType() : arrayType.GetGenericArguments()[0]; GroboIL il = context.Il; EmittingContext.LocalHolder arrayIndex = null; bool extendArray = extend && (CanAssign(zarr) || !isArray); bool extendArrayElement = extend && itemType.IsClass; var result = false; if (!extendArray) { result |= ExpressionEmittersCollection.Emit(zarr, context, returnDefaultValueLabel, ResultType.Value, extend, out arrayType); // stack: [array] if (context.Options.HasFlag(CompilerOptions.CheckNullReferences)) { result = true; il.Dup(); // stack: [array, array] il.Brfalse(returnDefaultValueLabel); // if(array == null) goto returnDefaultValue; stack: [array] } EmitLoadIndex(zindex, context, arrayType); // stack: [array, arrayIndex] if (context.Options.HasFlag(CompilerOptions.CheckArrayIndexes)) { result = true; arrayIndex = context.DeclareLocal(typeof(int)); il.Stloc(arrayIndex); // arrayIndex = index; stack: [array] il.Dup(); // stack: [array, array] if (isArray) { il.Ldlen(); // stack: [array, array.Length] } else { EmitLoadField(context, arrayType, arrayType.GetField("_size", BindingFlags.Instance | BindingFlags.NonPublic)); } il.Ldloc(arrayIndex); // stack: [array, array.Length, arrayIndex] il.Ble(returnDefaultValueLabel, false); // if(array.Length <= arrayIndex) goto returnDefaultValue; stack: [array] il.Ldloc(arrayIndex); // stack: [array, arrayIndex] il.Ldc_I4(0); // stack: [array, arrayIndex, 0] il.Blt(returnDefaultValueLabel, false); // if(arrayIndex < 0) goto returnDefaultValue; stack: [array] } else if (extendArrayElement || !isArray) { arrayIndex = context.DeclareLocal(typeof(int)); il.Stloc(arrayIndex); // arrayIndex = index; stack: [array] } } else { EmittingContext.LocalHolder arrayOwner = null; switch (zarr.NodeType) { case ExpressionType.Parameter: case ExpressionType.ArrayIndex: case ExpressionType.Index: Type type; ExpressionEmittersCollection.Emit(zarr, context, returnDefaultValueLabel, ResultType.ByRefAll, true, out type); // stack: [ref array] arrayOwner = context.DeclareLocal(type); il.Dup(); // stack: [ref array, ref array] il.Stloc(arrayOwner); // arrayOwner = ref array; stack: [ref array] il.Ldind(zarr.Type); // stack: [array] break; case ExpressionType.MemberAccess: var memberExpression = (MemberExpression)zarr; Type memberType; context.EmitMemberAccess(memberExpression, returnDefaultValueLabel, context.Options.HasFlag(CompilerOptions.CheckNullReferences), true, ResultType.ByRefValueTypesOnly, out memberType, out arrayOwner); // stack: [array] break; default: throw new InvalidOperationException("Cannot extend array for expression with node type '" + zarr.NodeType + "'"); } if (context.Options.HasFlag(CompilerOptions.CheckNullReferences)) { il.Dup(); // stack: [array, array] il.Brfalse(returnDefaultValueLabel); // if(array == null) goto returnDefaultValue; stack: [array] } EmitLoadIndex(zindex, context, arrayType); result = true; arrayIndex = context.DeclareLocal(typeof(int)); il.Stloc(arrayIndex); // arrayIndex = index; stack: [array] il.Ldloc(arrayIndex); // stack: [array, arrayIndex] il.Ldc_I4(0); // stack: [array, arrayIndex, 0] il.Blt(returnDefaultValueLabel, false); // if(arrayIndex < 0) goto returnDefaultValue; stack: [array] il.Dup(); // stack: [array, array] if (isArray) { il.Ldlen(); // stack: [array, array.Length] } else { EmitLoadField(context, arrayType, arrayType.GetField("_size", BindingFlags.Instance | BindingFlags.NonPublic)); } il.Ldloc(arrayIndex); // stack: [array, array.Length, arrayIndex] var bigEnoughLabel = il.DefineLabel("bigEnough"); il.Bgt(bigEnoughLabel, false); // if(array.Length > arrayIndex) goto bigEnough; stack: [array] using (var array = context.DeclareLocal(arrayType)) { il.Stloc(array); // stack: [] if (!isArray) { EnsureCount(context, array, arrayIndex, arrayType); } else { il.Ldloca(array); // stack: [ref array] il.Ldloc(arrayIndex); // stack: [ref array, arrayIndex] il.Ldc_I4(1); // stack: [ref array, arrayIndex, 1] il.Add(); // stack: [ref array, arrayIndex + 1] il.Call(arrayResizeMethod.MakeGenericMethod(arrayType.GetElementType())); // Array.Resize(ref array, 1 + arrayIndex); stack: [] switch (zarr.NodeType) { case ExpressionType.Parameter: case ExpressionType.ArrayIndex: case ExpressionType.Index: il.Ldloc(arrayOwner); // stack: [ref parameter] il.Ldloc(array); // stack: [ref parameter, array] il.Stind(arrayType); // parameter = array; stack: [] break; case ExpressionType.MemberAccess: var memberExpression = (MemberExpression)zarr; if (memberExpression.Expression != null) { il.Ldloc(arrayOwner); } il.Ldloc(array); switch (memberExpression.Member.MemberType) { case MemberTypes.Field: il.Stfld((FieldInfo)memberExpression.Member); break; case MemberTypes.Property: var propertyInfo = (PropertyInfo)memberExpression.Member; var setter = propertyInfo.GetSetMethod(context.SkipVisibility); if (setter == null) { throw new MissingMethodException(propertyInfo.ReflectedType.ToString(), "set_" + propertyInfo.Name); } il.Call(setter, memberExpression.Expression == null ? null : memberExpression.Expression.Type); break; default: throw new NotSupportedException("Member type '" + memberExpression.Member.MemberType + "' is not supported"); } break; default: throw new InvalidOperationException("Unable to assign array to an expression with node type '" + zarr.NodeType); } } il.Ldloc(array); context.MarkLabelAndSurroundWithSP(bigEnoughLabel); } } if (!isArray) { // TODO: это злобно, лист при всех операциях меняет _version, а мы нет EmitLoadField(context, arrayType, arrayType.GetField("_items", BindingFlags.Instance | BindingFlags.NonPublic)); arrayType = itemType.MakeArrayType(); } if (extendArrayElement) { // stack: [array] var constructor = itemType.GetConstructor(Type.EmptyTypes); if (itemType.IsArray || constructor != null) { using (var array = context.DeclareLocal(arrayType)) { il.Dup(); // stack: [array, array] il.Stloc(array); // stack: [array] il.Ldloc(arrayIndex); // stack: [array, arrayIndex] il.Ldelem(itemType); // stack: [array[arrayIndex]] var elementIsNotNullLabel = il.DefineLabel("elementIsNotNull"); il.Brtrue(elementIsNotNullLabel); il.Ldloc(array); il.Ldloc(arrayIndex); context.Create(itemType); il.Stelem(itemType); context.MarkLabelAndSurroundWithSP(elementIsNotNullLabel); il.Ldloc(array); } } } if (arrayIndex != null) { il.Ldloc(arrayIndex); arrayIndex.Dispose(); } switch (whatReturn) { case ResultType.ByRefAll: il.Ldelema(itemType); resultType = itemType.MakeByRefType(); break; case ResultType.ByRefValueTypesOnly: if (itemType.IsValueType) { il.Ldelema(itemType); resultType = itemType.MakeByRefType(); } else { il.Ldelem(itemType); // stack: [array[arrayIndex]] resultType = itemType; } break; default: il.Ldelem(itemType); // stack: [array[arrayIndex]] resultType = itemType; break; } return(result); }
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>))); }
internal static void Resize(GroboIL il, GroboIL.Local buffer, GroboIL.Local offset /*, GroboIL.Local len*/) { var totalLen = il.DeclareLocal(typeof(int)); //offset add typesize = minSize il.Ldloc(offset); il.Add(); il.Stloc(totalLen); var exit = il.DefineLabel("exit"); //Compare if minSize < buffer.len il.Ldloc(totalLen); il.Ldloc(buffer); il.Ldlen(); il.Clt(false); il.Brtrue(exit); var avar = il.DeclareLocal(typeof(byte)); il.Ldc_I4(2); il.Stloc(avar); var point = il.DefineLabel("for_label"); il.MarkLabel(point); //body il.Ldloc(avar); il.Ldc_I4(2); il.Mul(); il.Stloc(avar); //end body il.Ldloc(buffer); il.Ldlen(); il.Ldloc(avar); il.Mul(); //OutputValue(il,typeof(int)); il.Ldloc(totalLen); //OutputValue(il, typeof(int)); il.Clt(false); //OutputValue(il, typeof(bool)); il.Brtrue(point); il.Ldloca(buffer); il.Ldloc(avar); il.Ldloc(buffer); il.Ldlen(); il.Mul(); //OutputValue(il, typeof(int)); il.Call(typeof(Array).GetMethod("Resize", BindingFlags.Public | BindingFlags.Static).MakeGenericMethod(typeof(byte))); il.MarkLabel(exit); }