Search code examples
c#decompiler

CallSite <>p__site while decompiling large code base


I have large code base from a client whose developer refused to provide the code.

I have de-compiled major portion of it, although I am having problem with following

I am using dnSpy, Just Decompile, dotPeek to decompile the code. I have tried to google and looked over Stackoverflow as well but could not find any thing that could answer me. I would really appreciate if someone can point me in right direction.

Update: I have pasted complete function

public virtual string ParseDealerInventoryLink(string toParseLinkData)
        {
            string pattern = "{([^}]+)}";
            string text = toParseLinkData;
            RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Compiled;
            if (this != null && !string.IsNullOrWhiteSpace(text))
            {
                object dyn = new PropertyBag(this);
                Regex regex = new Regex(pattern, options);
                Dictionary<string, DealerInventoryLinkPatternParam> linkPatternMap = DealerInventoryFeedUrlParser.GetLinkPatternParamValueMap();
                text = regex.Replace(text, delegate(Match mat)
                {
                    if (mat.Success && mat.Groups.Count > 0)
                    {
                        Group group = mat.Groups[mat.Groups.Count - 1];
                        if (group.Success)
                        {
                            string value = group.Value;
                            if (linkPatternMap.ContainsKey(value))
                            {
                                if (DealerInventoryFeedUrlParser.<ParseDealerInventoryLink>o__SiteContainer0.<>p__Site1 == null)
                                {
                                    DealerInventoryFeedUrlParser.<ParseDealerInventoryLink>o__SiteContainer0.<>p__Site1 = CallSite<Func<CallSite, object, string>>.Create(Microsoft.CSharp.RuntimeBinder.Binder.Convert(CSharpBinderFlags.None, typeof(string), typeof(DealerInventoryFeedUrlParser)));
                                }
                                Func<CallSite, object, string> target = DealerInventoryFeedUrlParser.<ParseDealerInventoryLink>o__SiteContainer0.<>p__Site1.Target;
                                CallSite <>p__Site = DealerInventoryFeedUrlParser.<ParseDealerInventoryLink>o__SiteContainer0.<>p__Site1;
                                if (DealerInventoryFeedUrlParser.<ParseDealerInventoryLink>o__SiteContainer0.<>p__Site2 == null)
                                {
                                    DealerInventoryFeedUrlParser.<ParseDealerInventoryLink>o__SiteContainer0.<>p__Site2 = CallSite<Func<CallSite, Type, object, object>>.Create(Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(CSharpBinderFlags.None, "ToString", null, typeof(DealerInventoryFeedUrlParser), new CSharpArgumentInfo[]
                                    {
                                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.IsStaticType, null),
                                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
                                    }));
                                }
                                Func<CallSite, Type, object, object> target2 = DealerInventoryFeedUrlParser.<ParseDealerInventoryLink>o__SiteContainer0.<>p__Site2.Target;
                                CallSite <>p__Site2 = DealerInventoryFeedUrlParser.<ParseDealerInventoryLink>o__SiteContainer0.<>p__Site2;
                                Type typeFromHandle = typeof(Convert);
                                if (DealerInventoryFeedUrlParser.<ParseDealerInventoryLink>o__SiteContainer0.<>p__Site3 == null)
                                {
                                    DealerInventoryFeedUrlParser.<ParseDealerInventoryLink>o__SiteContainer0.<>p__Site3 = CallSite<Func<CallSite, object, string, object>>.Create(Microsoft.CSharp.RuntimeBinder.Binder.GetIndex(CSharpBinderFlags.None, typeof(DealerInventoryFeedUrlParser), new CSharpArgumentInfo[]
                                    {
                                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
                                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null)
                                    }));
                                }
                                return target(<>p__Site, target2(<>p__Site2, typeFromHandle, DealerInventoryFeedUrlParser.<ParseDealerInventoryLink>o__SiteContainer0.<>p__Site3.Target(DealerInventoryFeedUrlParser.<ParseDealerInventoryLink>o__SiteContainer0.<>p__Site3, dyn, linkPatternMap[value].ColumnName)));
                            }
                        }
                    }
                    return mat.Value;
                });
            }
            return text;
        }

Solution

  • class DealerInventoryFeedUrlParser
    {
        void ParseDealerInventoryLink()
        {
            dynamic input = new object();
            string link = input;
        }
    }
    

    generates the following when compiled and then decompiled in ILSpy:

    class DealerInventoryFeedUrlParser
    {
        private void ParseDealerInventoryLink()
        {
            object input = new object();
            if (<>o__0.<>p__0 == null)
            {
                <>o__0.<>p__0 = CallSite<Func<CallSite, object, string>>.Create(Binder.Convert(CSharpBinderFlags.None, typeof(string), typeof(DealerInventoryFeedUrlParser)));
            }
            string link = <>o__0.<>p__0.Target(<>o__0.<>p__0, input);
        }
      }
    

    ... which I would classify as "close enough".

    There is no particular reason I chose new object() as the value assigned to dynamic. This could have been an int, string - anything. The CallSite ininitialisation code would look the same. T2 in CallSite<Func<T1, T2, TResult>> is always object, so you won't know what your real input value is unless you look at the location in your decompiled source where DealerInventoryFeedUrlParser.<ParseDealerInventoryLink>o__SiteContainer0.<>p__Site1 is actually used.

    EDIT

    Now that the full source has been posted, let's try to put the pieces together:

    class DealerInventoryFeedUrlParser
    {
        static Dictionary<string, DealerInventoryLinkPatternParam> GetLinkPatternParamValueMap() => throw new NotImplementedException();
    
        public virtual string ParseDealerInventoryLink(string toParseLinkData)
        {
            string pattern = "{([^}]+)}";
            string text = toParseLinkData;
            RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Compiled;
            if (/*this != null &&*/ !string.IsNullOrWhiteSpace(text))
            {
                dynamic dyn = new PropertyBag(this);
                Regex regex = new Regex(pattern, options);
                Dictionary<string, DealerInventoryLinkPatternParam> linkPatternMap = GetLinkPatternParamValueMap();
                text = regex.Replace(text, delegate (Match mat)
                {
                    if (mat.Success && mat.Groups.Count > 0)
                    {
                        Group group = mat.Groups[mat.Groups.Count - 1];
                        if (group.Success)
                        {
                            string value = group.Value;
                            if (linkPatternMap.ContainsKey(value))
                            {
                                return Convert.ToString(dyn[linkPatternMap[value].ColumnName]);
                            }
                        }
                    }
                    return mat.Value;
                });
            }
            return text;
        }
    }
    
    class PropertyBag
    {
        private readonly object Owner;
    
        public PropertyBag(object obj)
        {
            Owner = obj;
        }
    }
    
    class DealerInventoryLinkPatternParam
    {
        public string ColumnName { get; set; }
    }
    

    ParseDealerInventoryLink compiled/decompiled:

    public virtual string ParseDealerInventoryLink(string toParseLinkData)
    {
        string pattern = "{([^}]+)}";
        string text = toParseLinkData;
        RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Compiled;
        if (!string.IsNullOrWhiteSpace(text))
        {
            dynamic dyn = new PropertyBag(this);
            Regex regex = new Regex(pattern, options);
            Dictionary<string, DealerInventoryLinkPatternParam> linkPatternMap = DealerInventoryFeedUrlParser.GetLinkPatternParamValueMap();
            text = regex.Replace(text, delegate(Match mat)
            {
                if (mat.Success && mat.Groups.Count > 0)
                {
                    Group group = mat.Groups[mat.Groups.Count - 1];
                    if (group.Success)
                    {
                        string value = group.Value;
                        if (linkPatternMap.ContainsKey(value))
                        {
                            if (<>o__1.<>p__2 == null)
                            {
                                <>o__1.<>p__2 = CallSite<Func<CallSite, object, string>>.Create(Binder.Convert(CSharpBinderFlags.None, typeof(string), typeof(DealerInventoryFeedUrlParser)));
                            }
                            Func<CallSite, object, string> target = <>o__1.<>p__2.Target;
                            CallSite<Func<CallSite, object, string>> <>p__ = <>o__1.<>p__2;
                            if (<>o__1.<>p__1 == null)
                            {
                                <>o__1.<>p__1 = CallSite<Func<CallSite, Type, object, object>>.Create(Binder.InvokeMember(CSharpBinderFlags.None, "ToString", null, typeof(DealerInventoryFeedUrlParser), new CSharpArgumentInfo[2]
                                {
                                    CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.IsStaticType, null),
                                    CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
                                }));
                            }
                            Func<CallSite, Type, object, object> target2 = <>o__1.<>p__1.Target;
                            CallSite<Func<CallSite, Type, object, object>> <>p__2 = <>o__1.<>p__1;
                            Type typeFromHandle = typeof(Convert);
                            if (<>o__1.<>p__0 == null)
                            {
                                <>o__1.<>p__0 = CallSite<Func<CallSite, object, string, object>>.Create(Binder.GetIndex(CSharpBinderFlags.None, typeof(DealerInventoryFeedUrlParser), new CSharpArgumentInfo[2]
                                {
                                    CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
                                    CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null)
                                }));
                            }
                            return target(<>p__, target2(<>p__2, typeFromHandle, <>o__1.<>p__0.Target(<>o__1.<>p__0, dyn, linkPatternMap[value].ColumnName)));
                        }
                    }
                }
                return mat.Value;
            });
        }
        return text;
    }
    

    You can substitute the compiler-generated names to check whether I've gotten everything right. What I made is an honest, but very, very quick attempt at guessing what I'm looking at based on my limited understanding of dynamic. Now it's up to you to check everything, verify correctness and fix if necessary.

    P.S. The this != null check bit baffled me a little. Must be a quirk of the decompiler. I've commented it out as it didn't seem significant.

    P.P.S. Decompiling and recompiling binaries is a pretty dirty and error-prone process. If the changes you need to make are small, consider looking into .NET binary patching (Google it), i.e. by using ILSpy + Reflexil.

    Good luck.