Ejemplo n.º 1
0
        public void EmitWrite(WriterEmitContext context)
        {
            using (context.GetLocal(typeof(Span <T>), out var span))
            {
                context.LoadValue();
                context.Il.EmitCall(OpCodes.Call, Type.GetProperty(nameof(Memory <T> .Span)).GetGetMethod(), null);
                context.Il.Store(span);

                using (context.OverrideScope(spanWriter, span))
                {
                    spanWriter.EmitWrite(context);
                }
            }
        }
Ejemplo n.º 2
0
        protected void Enumerate(WriterEmitContext context, Action consumeValue)
        {
            var il = context.Il;

            var getEnumerator = Type.GetMethod(nameof(IEnumerable <int> .GetEnumerator), BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance);

            var enumeratorType        = getEnumerator.ReturnType;
            var enumeratorCurrentType = enumeratorType.GetProperty(nameof(IEnumerator <int> .Current)).PropertyType;

            var isEnumeratorByRef = enumeratorCurrentType.IsByRef;

            var current  = enumeratorType.GetProperty(nameof(IEnumerator <int> .Current)).GetMethod;
            var moveNext = enumeratorType.GetMethod(nameof(IEnumerator <int> .MoveNext), BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance);

            if (moveNext == null && enumeratorType.IsGenericType && enumeratorType.GetGenericTypeDefinition() == typeof(IEnumerator <>))
            {
                moveNext = typeof(IEnumerator).GetMethod(nameof(IEnumerator.MoveNext));
            }

            using (context.GetLocal(enumeratorType, out var enumerator))
            {
                context.LoadValue();
                il.EmitCall(enumeratorType.IsValueType ? OpCodes.Call : OpCodes.Callvirt, getEnumerator, null);
                il.Store(enumerator);

                // try
                var enumeratorIsDisposable = typeof(IDisposable).IsAssignableFrom(enumeratorType);
                if (enumeratorIsDisposable)
                {
                    il.BeginExceptionBlock();
                }

                // iterate over values

                var processLbl   = il.DefineLabel();
                var loadLbl      = il.DefineLabel();
                var outOfFinally = il.DefineLabel();

                il.Emit(OpCodes.Br, loadLbl);

                il.MarkLabel(processLbl);

                // true, false
                if (itemWriter.RequiresAddress & !isEnumeratorByRef)
                {
                    using (context.GetLocal(itemWriter.Type, out var value))
                    {
                        il.CallOn(enumerator, current);
                        il.Store(value);

                        using (context.OverrideScope(itemWriter, value))
                        {
                            consumeValue();
                        }
                    }
                }

                // true, true
                // false, false
                if (itemWriter.RequiresAddress ^ isEnumeratorByRef == false)
                {
                    void LoadValue()
                    {
                        il.CallOn(enumerator, current);
                    }

                    using (context.Scope(LoadValue, false))
                    {
                        consumeValue();
                    }
                }

                // false, true
                if (!itemWriter.RequiresAddress & isEnumeratorByRef)
                {
                    void LoadValue()
                    {
                        il.CallOn(enumerator, current);
                        il.LoadIndirect(itemWriter.Type);
                    }

                    using (context.Scope(LoadValue, false))
                    {
                        consumeValue();
                    }
                }

                il.MarkLabel(loadLbl);

                il.CallOn(enumerator, moveNext);

                il.Emit(OpCodes.Brtrue, processLbl);

                if (enumeratorIsDisposable)
                {
                    il.Emit(OpCodes.Leave, outOfFinally);

                    // finally
                    il.BeginFinallyBlock();
                    var nullLbl = il.DefineLabel();

                    il.Load(enumerator);
                    il.Emit(OpCodes.Brfalse, nullLbl);

                    il.Load(enumerator);
                    il.EmitCall(OpCodes.Callvirt, typeof(IDisposable).GetMethod(nameof(IDisposable.Dispose)), null);
                    il.MarkLabel(nullLbl);

                    il.EndExceptionBlock();

                    il.MarkLabel(outOfFinally);
                }
            }
        }