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); } } }
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); } } }