22

The code found in the PresentationCore.dll (.NET4 WPF) by ILSpy:

// MS.Internal.PresentationCore.BindUriHelper
internal static string UriToString(Uri uri)
{
    if (uri == null)
    {
        throw new ArgumentNullException("uri");
    }

    return new StringBuilder(uri.GetComponents(uri.IsAbsoluteUri ? UriComponents.AbsoluteUri : UriComponents.SerializationInfoString, UriFormat.SafeUnescaped), 2083).ToString();
}

The return type of uri.GetComponents is string, why didn't the method just return the string value instead of wrapping it in a StringBuilder(string).ToString(); Is this by design? What would be the reason for doing this in a general sense? Would it reduce allocations or improve Garbage Collection or used for thread safety?

George Stocker
  • 57,289
  • 29
  • 176
  • 237
Aimeast
  • 1,609
  • 14
  • 29
  • Do you believe `ILSpy` so much? This is decompiled code. You can explore the sources from http://referencesource.microsoft.com/netframework.aspx – Hamlet Hakobyan Jan 31 '14 at 11:56
  • 2
    It makes no sense. (Incidentally, 2083 is the maximum URL length supported by Internet Explorer. But knowing that, it still makes no sense.) – Matthew Watson Jan 31 '14 at 11:56
  • 7
    @HamletHakobyan Why would one not "believe" a decompiler? Are decompilers known for lying? –  Jan 31 '14 at 12:00
  • @delnan Look at closer. The keyword is `so much`. – Hamlet Hakobyan Jan 31 '14 at 12:01
  • @HamletHakobyan I'm afraid you'll have to elaborate. Are you saying the decompiler is wrong? Can one trust the decompiler in some cases but not in others, and how do I know which one applies? –  Jan 31 '14 at 12:02
  • 4
    @HamletHakobyan If the decompiler says that StringBuilder is being called, then StringBuilder is being called. There's no need to go to the framework sources. – Matthew Watson Jan 31 '14 at 12:03
  • 4
    Congrats OP.. you've asked a question where the only answer is that developer was drunk on the day they implemented that function. There is absolutely no reason that I can think of as to why this needs to happen. Not even "thread safety" comes into it.. given that strings are inherently thread safe (unless somewhere in that assembly there is some unsafe stuff going on). – Simon Whitehead Jan 31 '14 at 12:22
  • For the records, the reference source lists the exact same code, except for some more whitespace (linebreaks), and the magic number `2083` being a public constant `MAX_URL_LENGTH`. – poke Jan 31 '14 at 12:24
  • @delnan Decompilers get things wrong occasionally. For example `(object)string1 == (object)string2` gets decompiled as `string1 == string2` in some decompilers. Some other "why is .net doing this" questions were answered with "it's a bug in the decompiler". – CodesInChaos Jan 31 '14 at 12:32
  • @CodesInChaos You mean `\u003CMyClass\u003E_instanceOfMyClass` isn't valid C#? :P – Simon Whitehead Jan 31 '14 at 12:36
  • 1
    @SimonWhitehead I'm not talking about cases where identifiers are just invalid, those still show easily readable code matching the actual behaviour, but rather about cases where the decompiled code is subtly wrong, for example because it chooses a different overload (as in my earlier example) or the decompiler messed up the control flow. – CodesInChaos Jan 31 '14 at 12:38
  • `Resharpner` Decompiler gives same code only difference is `return ((object) new StringBuilder(...)` – Mujahid Daud Khan Jan 31 '14 at 12:45
  • similar class for `java` `return uri == null ? null : uri.toString();` ref:[Source Code](https://android.googlesource.com/platform/packages/apps/ContactsCommon/+/589bf5030f9ecd687b8796a4571a9b1a3c4c6740/src/com/android/contacts/common/util/UriUtils.java) – Mujahid Daud Khan Jan 31 '14 at 12:53

2 Answers2

1

Only thing I can think of is that if the first parameter being passed into the stringBuilder is null, then the stringbuilder will return string.empty rather than null (see http://msdn.microsoft.com/en-us/library/zb91weab(v=vs.100).aspx)

A string is nullable though... so why bother?!

Just doing a check and returning an empty string yourself would be a lot more efficient than newing up a stringBuilder instance.

The second parameter is just a suggested size that the stringbuilder should be initialized to...

Comments on the OP's question are right, it appears to be overkill.

Jay
  • 9,561
  • 7
  • 51
  • 72
  • 2
    Could be. Of course, now you could just do `return uri.GetComponents(uri.IsAbsoluteUri ? UriComponents.AbsoluteUri : UriComponents.SerializationInfoString, UriFormat.SafeUnescaped) ?? "";` But I don't think `Uri.GetComponents()` can return null anyway... – Matthew Watson Jan 31 '14 at 12:16
-2
/// <SecurityNote> 
/// Critical: Calls the native InternetGetCookieEx(). There is potential for information disclosure. 
/// Safe: A WebPermission demand is made for the given URI.
/// </SecurityNote> 
[SecurityCritical, SecurityTreatAsSafe]
[FriendAccessAllowed] // called by PF.Application.GetCookie()
[SuppressMessage("Microsoft.Interoperability", "CA1404:CallGetLastErrorImmediatelyAfterPInvoke",
    Justification="It's okay now. Be careful on change.")] 
internal static string GetCookie(Uri uri, bool throwIfNoCookie)
{ 
    // Always demand in order to prevent any cross-domain information leak. 
    SecurityHelper.DemandWebPermission(uri);

    UInt32 size = 0;
    string uriString = BindUriHelper.UriToString(uri);
    if (UnsafeNativeMethods.InternetGetCookieEx(uriString, null, null, ref size, 0, IntPtr.Zero))
    { 
        Debug.Assert(size > 0);
        size++; 
        System.Text.StringBuilder sb = new System.Text.StringBuilder((int)size); 
        // PresentationHost intercepts InternetGetCookieEx(). It will set the INTERNET_COOKIE_THIRD_PARTY
        // flag if necessary. 
        if (UnsafeNativeMethods.InternetGetCookieEx(uriString, null, sb, ref size, 0, IntPtr.Zero))
        {
            return sb.ToString();
        } 
    }
    if (!throwIfNoCookie && Marshal.GetLastWin32Error() == NativeMethods.ERROR_NO_MORE_ITEMS) 
        return null; 
    throw new Win32Exception(/*uses last error code*/);
}

/// <SecurityNote> 
/// Critical: Sets cookies via the native InternetSetCookieEx(); doesn't demand WebPermission for the given 
///     URI. This creates danger of overwriting someone else's cookies.
///     The P3P header has to be from an authentic web response in order to be trusted at all. 
/// </SecurityNote>
[SecurityCritical]
private static bool SetCookieUnsafe(Uri uri, string cookieData, string p3pHeader)
{ 
    string uriString = BindUriHelper.UriToString(uri);
    // PresentationHost intercepts InternetSetCookieEx(). It will set the INTERNET_COOKIE_THIRD_PARTY 
    // flag if necessary. (This doesn't look very elegant but is much simpler than having to make the 
    // 3rd party decision here as well or calling into the native code (from PresentationCore).)
    uint res = UnsafeNativeMethods.InternetSetCookieEx( 
        uriString, null, cookieData, UnsafeNativeMethods.INTERNET_COOKIE_EVALUATE_P3P, p3pHeader);
    if(res == 0)
        throw new Win32Exception(/*uses last error code*/);
    return res != UnsafeNativeMethods.COOKIE_STATE_REJECT; 
}

    private const int MAX_PATH_LENGTH = 2048 ; 
    private const int MAX_SCHEME_LENGTH = 32;
    public const int MAX_URL_LENGTH = MAX_PATH_LENGTH + MAX_SCHEME_LENGTH + 3; /*=sizeof("://")*/ 

    //
    // Uri-toString does 3 things over the standard .toString()
    // 
    //  1) We don't unescape special control characters. The default Uri.ToString()
    //     will unescape a character like ctrl-g, or ctrl-h so the actual char is emitted. 
    //     However it's considered safer to emit the escaped version. 
    //
    //  2) We truncate urls so that they are always <= MAX_URL_LENGTH 
    //
    // This method should be called whenever you are taking a Uri
    // and performing a p-invoke on it.
    // 
    internal static string UriToString(Uri uri)
    { 
        if (uri == null) 
        {
            throw new ArgumentNullException("uri"); 
        }

        return new StringBuilder(
            uri.GetComponents( 
                uri.IsAbsoluteUri ? UriComponents.AbsoluteUri : UriComponents.SerializationInfoString,
                UriFormat.SafeUnescaped), 
            MAX_URL_LENGTH).ToString(); 
    }
ChenYa
  • 5
  • 2
  • 2
    I don't get it. `We truncate urls so that they are always <= MAX_URL_LENGTH`. The code doesn't do that. `StringBuilder(string, Int32)` sets the suggested starting size, not the max size. – qxn Jan 31 '14 at 13:38
  • It's clearly a bug that Microsoft are unaware of, or don't see the need to fix. – Matthew Watson Jan 31 '14 at 14:38