protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { using (Compiler.Local oldList = AppendToCollection ? ctx.GetLocalWithValue(ExpectedType, valueFrom) : null) using (Compiler.Local builder = new Compiler.Local(ctx, builderFactory.ReturnType)) { ctx.EmitCall(builderFactory); ctx.StoreValue(builder); if (AppendToCollection) { Compiler.CodeLabel done = ctx.DefineLabel(); if (!Helpers.IsValueType(ExpectedType)) { ctx.LoadValue(oldList); ctx.BranchIfFalse(done, false); // old value null; nothing to add } #if COREFX TypeInfo typeInfo = ExpectedType.GetTypeInfo(); #else Type typeInfo = ExpectedType; #endif PropertyInfo prop = Helpers.GetProperty(typeInfo, "Length", false); if (prop == null) { prop = Helpers.GetProperty(typeInfo, "Count", false); } #if !NO_GENERICS if (prop == null) { prop = Helpers.GetProperty(ResolveIReadOnlyCollection(ExpectedType, Tail.ExpectedType), "Count", false); } #endif ctx.LoadAddress(oldList, oldList.Type); ctx.EmitCall(Helpers.GetGetMethod(prop, false, false)); ctx.BranchIfFalse(done, false); // old list is empty; nothing to add Type voidType = ctx.MapType(typeof(void)); if (addRange != null) { ctx.LoadValue(builder); ctx.LoadValue(oldList); ctx.EmitCall(addRange); if (addRange.ReturnType != null && add.ReturnType != voidType) { ctx.DiscardValue(); } } else { // loop and call Add repeatedly MethodInfo moveNext, current, getEnumerator = GetEnumeratorInfo(ctx.Model, out moveNext, out current); Helpers.DebugAssert(moveNext != null); Helpers.DebugAssert(current != null); Helpers.DebugAssert(getEnumerator != null); Type enumeratorType = getEnumerator.ReturnType; using (Compiler.Local iter = new Compiler.Local(ctx, enumeratorType)) { ctx.LoadAddress(oldList, ExpectedType); ctx.EmitCall(getEnumerator); ctx.StoreValue(iter); using (ctx.Using(iter)) { Compiler.CodeLabel body = ctx.DefineLabel(), next = ctx.DefineLabel(); ctx.Branch(next, false); ctx.MarkLabel(body); ctx.LoadAddress(builder, builder.Type); ctx.LoadAddress(iter, enumeratorType); ctx.EmitCall(current); ctx.EmitCall(add); if (add.ReturnType != null && add.ReturnType != voidType) { ctx.DiscardValue(); } ctx.MarkLabel(@next); ctx.LoadAddress(iter, enumeratorType); ctx.EmitCall(moveNext); ctx.BranchIfTrue(body, false); } } } ctx.MarkLabel(done); } EmitReadList(ctx, builder, Tail, add, packedWireType, false); ctx.LoadAddress(builder, builder.Type); ctx.EmitCall(finish); if (ExpectedType != finish.ReturnType) { ctx.Cast(ExpectedType); } } }
MethodInfo GetEnumeratorInfo(TypeModel model, out MethodInfo moveNext, out MethodInfo current) { #if WINRT TypeInfo enumeratorType = null, iteratorType, expectedType = ExpectedType.GetTypeInfo(); #else Type enumeratorType = null, iteratorType, expectedType = ExpectedType; #endif // try a custom enumerator MethodInfo getEnumerator = Helpers.GetInstanceMethod(expectedType, "GetEnumerator", null); Type itemType = Tail.ExpectedType; if (getEnumerator != null) { iteratorType = getEnumerator.ReturnType #if WINRT .GetTypeInfo() #endif ; moveNext = Helpers.GetInstanceMethod(iteratorType, "MoveNext", null); PropertyInfo prop = Helpers.GetProperty(iteratorType, "Current", false); current = prop == null ? null : Helpers.GetGetMethod(prop, false, false); if (moveNext == null && (model.MapType(ienumeratorType).IsAssignableFrom(iteratorType))) { moveNext = Helpers.GetInstanceMethod(model.MapType(ienumeratorType), "MoveNext", null); } // fully typed if (moveNext != null && moveNext.ReturnType == model.MapType(typeof(bool)) && current != null && current.ReturnType == itemType) { return(getEnumerator); } moveNext = current = getEnumerator = null; } #if !NO_GENERICS // try IEnumerable<T> Type tmp = model.MapType(typeof(System.Collections.Generic.IEnumerable <>), false); if (tmp != null) { tmp = tmp.MakeGenericType(itemType); #if WINRT enumeratorType = tmp.GetTypeInfo(); #else enumeratorType = tmp; #endif } ; if (enumeratorType != null && enumeratorType.IsAssignableFrom(expectedType)) { getEnumerator = Helpers.GetInstanceMethod(enumeratorType, "GetEnumerator"); #if WINRT iteratorType = getEnumerator.ReturnType.GetTypeInfo(); #else iteratorType = getEnumerator.ReturnType; #endif moveNext = Helpers.GetInstanceMethod(model.MapType(ienumeratorType), "MoveNext"); current = Helpers.GetGetMethod(Helpers.GetProperty(iteratorType, "Current", false), false, false); return(getEnumerator); } #endif // give up and fall-back to non-generic IEnumerable enumeratorType = model.MapType(ienumerableType); getEnumerator = Helpers.GetInstanceMethod(enumeratorType, "GetEnumerator"); iteratorType = getEnumerator.ReturnType #if WINRT .GetTypeInfo() #endif ; moveNext = Helpers.GetInstanceMethod(iteratorType, "MoveNext"); current = Helpers.GetGetMethod(Helpers.GetProperty(iteratorType, "Current", false), false, false); return(getEnumerator); }