public static EnumerableInfo FindMethods(Type t)
        {
            var ie = new EnumerableInfo();

            var methods = t.GetMethods(BindingFlags.Instance | BindingFlags.Public);

            var getEnumeratorCandidates = methods
                                          .Where(mi => mi.Name == "GetEnumerator" && mi.GetParameters().Length == 0 && mi.ReturnType != typeof(object) && mi.ReturnType != typeof(void))
                                          .ToList();

            var genericEnumerable = GetGenericEnumerable(t);

            if (genericEnumerable != null)
            {
                getEnumeratorCandidates.Add(genericEnumerable.GetMethod("GetEnumerator"));
            }

            getEnumeratorCandidates.Sort(SortEnumeratorCandidates);

            ie.GetEnumerator = getEnumeratorCandidates.FirstOrDefault();

            var genericCollection = GetGenericCollection(t);

            // only use get_Count if ICollection<> or IReadOnlyCollection<> are implemented
            if (genericCollection != null)
            {
                ie.get_Count = methods
                               .FirstOrDefault(mi => mi.IsSpecialName && mi.ReturnType == typeof(int) && mi.Name == "get_Count" && mi.GetParameters().Length == 0) ??
                               genericCollection.GetMethod("get_Count");
            }

            if (ie.GetEnumerator == null)
            {
                return(ie);
            }

            var enumeratorType = ie.GetEnumerator.ReturnType;

            methods = enumeratorType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

            ie.MoveNext    = methods.FirstOrDefault(mi => mi.Name == "MoveNext");
            ie.Dispose     = methods.FirstOrDefault(mi => mi.Name == "Dispose");
            ie.get_Current = methods.FirstOrDefault(mi => mi.IsSpecialName && mi.Name == "get_Current");

            if (ie.MoveNext == null && typeof(System.Collections.IEnumerator).IsAssignableFrom(enumeratorType))
            {
                ie.MoveNext = _MoveNext;
            }

            if (ie.Dispose == null && typeof(System.IDisposable).IsAssignableFrom(enumeratorType))
            {
                ie.Dispose = _Dispose;
            }

            return(ie);
        }
Esempio n. 2
0
        void EmitEnumerable(Schema schema, bool pushResult, bool isArray)
        {
            var        enumerable = EnumerableInfo.FindMethods(schema.NetType);
            MethodInfo getKey     = null;
            MethodInfo getValue   = null;

            if (!isArray)
            {
                getKey   = enumerable.get_Current.ReturnType.GetProperty("Key").GetMethod;
                getValue = enumerable.get_Current.ReturnType.GetProperty("Value").GetMethod;
            }

            var ifNotNull = DefineLabel("ifNotNull");
            var ifNull    = DefineLabel("ifNull");

            Emit.Duplicate();       // preserve the value
            Emit.BranchIfTrue(ifNotNull);
            Emit.Pop();             // discard value
            if (pushResult)
            {
                Emit.Pop();                         // discard avail
            }
            WriteConstant("null", push: pushResult);
            Emit.Branch(ifNull);

            Emit.MarkLabel(ifNotNull);

            int localDepth = pushResult ? 2 : 1;

            Depth += localDepth;

            // begin array
            WriteConstant(isArray ? "[" : "{");

            var loopBottom = DefineLabel("loopBottom");
            var loopTop    = DefineLabel("loopTop");
            var closeArray = DefineLabel("closeArray");


            // call GetEnumerator
            CallCorrectly(enumerable.GetEnumerator, schema.NetType);
            var enumeratorType = enumerable.GetEnumerator.ReturnType;

            // use the pointer for enumerator structs
            if (enumeratorType.IsValueType)
            {
                PushAddress(enumeratorType);
            }

            // FIXME need to have some support for Dispose
            //var exceptionBlock = enumerable.Dispose == null ? null : Emit.BeginExceptionBlock();

            // unroll the first iteration so the loop can always write the comma

            /* if (!enumerator.MoveNext()) goto closeArray; */
            Emit.Duplicate();             // preserve enumerator
            CallCorrectly(enumerable.MoveNext, enumeratorType);
            Emit.BranchIfFalse(closeArray);

            // push enumerator.Current
            Emit.Duplicate();             // preserve enumerator
            CallCorrectly(enumerable.get_Current, enumeratorType);

            if (!isArray)
            {
                // we've got a KeyValuePair<,>
                PushAddress(getValue.DeclaringType);

                Emit.Duplicate();                 // preserve KeyValuePair<,>*
                Emit.Call(getKey);
                // write the first key
                Depth++;
                EmitInline(schema.Keys);
                WriteConstant(":");
                Depth--;
                Emit.Call(getValue);
            }

            // write the first element
            EmitInline(schema.Items);

            Emit.Branch(loopBottom);
            Emit.MarkLabel(loopTop);

            // write ','
            WriteConstant(",");

            // push enumerator.Current
            Emit.Duplicate();            // preserve enumerator
            CallCorrectly(enumerable.get_Current, enumeratorType);

            if (!isArray)
            {
                // we've got a KeyValuePair<,>
                PushAddress(getValue.DeclaringType);

                Emit.Duplicate();                 // preserve KeyValuePair<,>*
                Emit.Call(getKey);
                // write the key
                Depth++;
                EmitInline(schema.Keys);
                WriteConstant(":");
                Depth--;
                Emit.Call(getValue);
            }

            // write the element
            EmitInline(schema.Items);


            /* endLoop: */
            Emit.MarkLabel(loopBottom);

            /* if (enumerator.MoveNext()) goto beginLoop; */
            Emit.Duplicate();             // preserve enumerator
            CallCorrectly(enumerable.MoveNext, enumeratorType);
            Emit.BranchIfTrue(loopTop);


            Emit.MarkLabel(closeArray);

            // this is where the finally block would go
            //if (exceptionBlock != null)
            //{
            //	var f = Emit.BeginFinallyBlock(exceptionBlock);
            //	CallCorrectly(enumerable.Dispose, enumeratorType);
            //	Emit.EndFinallyBlock(f);
            //	Emit.EndExceptionBlock(exceptionBlock);
            //}
            //else

            Emit.Pop();             // discard enumerator

            // end array
            Depth--;
            WriteConstant(isArray ? "]" : "}");
            Depth++;


            if (pushResult)
            {
                // push bytes written
                Emit.LoadLocal(LocalAvailable);
                Emit.Subtract();
            }

            if (schema.Nullable)
            {
                Emit.MarkLabel(ifNull);
            }

            Depth -= localDepth;
        }