This project is read-only.
1

Closed

DateTime in anonymous type causes InvalidProgramException

description

Setting EnableAnonymousTypes to true in the parameters causes static properties like DateTimeOffset.Now to NOT be filtered out by GetGetters leading to an InvalidProgramException.

As they're not filtered by GetGetters, Reflection.CreateGetMethod is called, but as DateTimeOffset is a struct the IL generated does not take into account static methods correctly. That leads to an InvalidProgramException when it attempts to call a static method with the object as the first argument.

FIX:

Primary: GetGetters should check whether the current type is an anonymous type or not. Example code is below.

Secondary: CreateGetMethod should maybe branch on getMethod.IsStatic before type.IsClass to create the correct IL for a static method in a struct. Less important here but possibly a bug either way.

Reproduction:
    [TestMethod]
    public void BreakFastJson()
    {
        var data = new List<DateTimeOffset>();
        data.Add(new DateTimeOffset(DateTime.Now));

        var anonTypeWithDateTimeOffset = data.Select(entry => new { DateTimeOffset = entry }).ToList();

        var jsonParameters = new JSONParameters { EnableAnonymousTypes = true };
        var json = JSON.ToJSON(anonTypeWithDateTimeOffset.First(), jsonParameters); // this will throw
    }
Fix:

(Note that this possibly makes JSONParameters.ShowReadOnlyProperties obsolete.)

Changes to Reflection.cs:

Add this helper
    private static bool IsAnonymousType(Type type)
    {
        // may break in the future if compiler defined names change...
        const string CS_ANONYMOUS_PREFIX = "<>f__AnonymousType";
        const string VB_ANONYMOUS_PREFIX = "VB$AnonymousType";

        if (type == null)
            throw new ArgumentNullException("type");

        if (type.Name.StartsWith(CS_ANONYMOUS_PREFIX, StringComparison.Ordinal) || type.Name.StartsWith(VB_ANONYMOUS_PREFIX, StringComparison.Ordinal))
        {
            return type.IsDefined(typeof(CompilerGeneratedAttribute), false);
        }

        return false;
    }
And update GetGetters to call it instead of using ShowReadOnlyProperties
    internal Getters[] GetGetters(Type type, bool ShowReadOnlyProperties, List<Type> IgnoreAttributes)//JSONParameters param)
    {
        Getters[] val = null;
        if (_getterscache.TryGetValue(type, out val))
            return val;

        bool isAnonymousType = IsAnonymousType(type);

        PropertyInfo[] props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
        List<Getters> getters = new List<Getters>();
        foreach (PropertyInfo p in props)
        {
            if (p.GetIndexParameters().Length > 0)
            {// Property is an indexer
                continue;
            }

            if (!p.CanWrite && isAnonymousType == false)
            {
                continue;
            }   
Closed Jul 15, 2016 at 10:38 AM by MGholam
Fixed in 2.1.17

comments

skottmckay wrote Oct 14, 2015 at 12:13 AM

Any chance this patch can be applied? Would rather not take a copy of fastJSON and have to modify it to fix this issue.