Add implementation for AssemblyName::EscapedCodeBase (#8072)
authorRahul Kumar <rahku@microsoft.com>
Sun, 13 Nov 2016 17:34:58 +0000 (09:34 -0800)
committerJan Kotas <jkotas@microsoft.com>
Sun, 13 Nov 2016 17:34:58 +0000 (09:34 -0800)
src/mscorlib/model.xml
src/mscorlib/ref/mscorlib.cs
src/mscorlib/src/System/Reflection/AssemblyName.cs

index 393a4bb26b2b7192f05c010b656b175b5d350c78..d43dd574d1c4967a0b7d84cce54ae4cd76ff2eb5 100644 (file)
       <Member Name="get_IsFullyTrusted" />
       <Member Name="get_IsDynamic" />
       <Member Name="get_CodeBase" />
+      <Member Name="get_EscapedCodeBase" />
       <Member Name="get_Location" />
       <Member Name="get_ManifestModule" />
       <Member Name="get_CustomAttributes" />
       <Member MemberType="Property" Name="GlobalAssemblyCache" />
       <Member MemberType="Property" Name="HostContext" />
       <Member MemberType="Property" Name="CodeBase" />
+      <Member MemberType="Property" Name="EscapedCodeBase" />
       <Member MemberType="Property" Name="ImageRuntimeVersion" />
       <Member MemberType="Property" Name="IsFullyTrusted" />
       <Member MemberType="Property" Name="Location" />
       <Member Name="get_CultureInfo" />
       <Member Name="get_CultureName" />
       <Member Name="get_CodeBase" />
+      <Member Name="get_EscapedCodeBase" />
       <Member Name="get_ContentType" />
       <Member Name="get_Flags" />
       <Member Name="get_FullName" />
       <Member MemberType="Property" Name="CultureInfo" />
       <Member MemberType="Property" Name="CultureName" />
       <Member MemberType="Property" Name="ContentType" />
+      <Member MemberType="Property" Name="EscapedCodeBase" />
       <Member MemberType="Property" Name="Flags" />
       <Member MemberType="Property" Name="FullName" />
       <Member MemberType="Property" Name="HashAlgorithm" />
index 4c364ada180d56ece8c7c4305fd1ba2866f3074a..f63433e0b41383432e17779804c7b84929f8096f 100644 (file)
@@ -7230,6 +7230,7 @@ namespace System.Reflection
         public virtual System.Collections.Generic.IEnumerable<System.Reflection.CustomAttributeData> CustomAttributes { get { throw null; } }
         public virtual System.Collections.Generic.IEnumerable<System.Reflection.TypeInfo> DefinedTypes { get { throw null; } }
         public virtual System.Reflection.MethodInfo EntryPoint { get { throw null; } }
+        public virtual string EscapedCodeBase { [System.Security.SecurityCriticalAttribute]get { throw null; } }
         public virtual System.Collections.Generic.IEnumerable<System.Type> ExportedTypes { get { throw null; } }
         public virtual string FullName { get { throw null; } }
         public virtual bool GlobalAssemblyCache { get { throw null; } }
@@ -7436,6 +7437,7 @@ namespace System.Reflection
         public System.Globalization.CultureInfo CultureInfo { get { throw null; } set { } }
         public string CultureName { [System.Security.SecurityCriticalAttribute]get { throw null; } [System.Security.SecurityCriticalAttribute]set { } }
         public string CodeBase { get { throw null; } set { } }
+        public string EscapedCodeBase { get { throw null; } }
         public System.Reflection.AssemblyNameFlags Flags { get { throw null; } set { } }
         public string FullName { [System.Security.SecuritySafeCriticalAttribute]get { throw null; } }
         public System.Configuration.Assemblies.AssemblyHashAlgorithm HashAlgorithm { get { throw null; } set { } }
index 4c3d5aa6d6090378f70d4e0757045e8c7b18641c..c45a4619ea5d4b6b6e9495a7545d344cf7da15ff 100644 (file)
@@ -24,6 +24,7 @@ namespace System.Reflection {
     using System.Runtime.InteropServices;
     using System.Runtime.Versioning;
     using System.Diagnostics.Contracts;
+    using System.Text;
 
     [Serializable]
     [ClassInterface(ClassInterfaceType.None)]
@@ -533,7 +534,228 @@ namespace System.Reflection {
         private extern byte[] nGetPublicKeyToken();
     
         [System.Security.SecurityCritical]  // auto-generated
-        [MethodImplAttribute(MethodImplOptions.InternalCall)]
-        static internal extern String EscapeCodeBase(String codeBase);
+        static internal String EscapeCodeBase(String codebase)
+        {
+            if (codebase == null)
+                return string.Empty;
+                
+            int position = 0;
+            char[] dest = EscapeString(codebase, 0, codebase.Length, null, ref position, true, c_DummyChar, c_DummyChar, c_DummyChar);
+            if (dest == null)
+                return codebase;
+
+            return new string(dest, 0, position);            
+        }
+
+        // This implementation of EscapeString has been copied from System.Private.Uri from corefx repo
+        // - forceX characters are always escaped if found
+        // - rsvd character will remain unescaped
+        //
+        // start    - starting offset from input
+        // end      - the exclusive ending offset in input
+        // destPos  - starting offset in dest for output, on return this will be an exclusive "end" in the output.
+        //
+        // In case "dest" has lack of space it will be reallocated by preserving the _whole_ content up to current destPos
+        //
+        // Returns null if nothing has to be escaped AND passed dest was null, otherwise the resulting array with the updated destPos
+        //
+        internal unsafe static char[] EscapeString(string input, int start, int end, char[] dest, ref int destPos,
+            bool isUriString, char force1, char force2, char rsvd)
+        {
+            int i = start;
+            int prevInputPos = start;
+            byte* bytes = stackalloc byte[c_MaxUnicodeCharsReallocate * c_MaxUTF_8BytesPerUnicodeChar];   // 40*4=160
+
+            fixed (char* pStr = input)
+            {
+                for (; i < end; ++i)
+                {
+                    char ch = pStr[i];
+
+                    // a Unicode ?
+                    if (ch > '\x7F')
+                    {
+                        short maxSize = (short)Math.Min(end - i, (int)c_MaxUnicodeCharsReallocate - 1);
+
+                        short count = 1;
+                        for (; count < maxSize && pStr[i + count] > '\x7f'; ++count)
+                            ;
+
+                        // Is the last a high surrogate?
+                        if (pStr[i + count - 1] >= 0xD800 && pStr[i + count - 1] <= 0xDBFF)
+                        {
+                            // Should be a rare case where the app tries to feed an invalid Unicode surrogates pair
+                            if (count == 1 || count == end - i)
+                                throw new FormatException(Environment.GetResourceString("Arg_FormatException"));
+                            // need to grab one more char as a Surrogate except when it's a bogus input
+                            ++count;
+                        }
+
+                        dest = EnsureDestinationSize(pStr, dest, i,
+                            (short)(count * c_MaxUTF_8BytesPerUnicodeChar * c_EncodedCharsPerByte),
+                            c_MaxUnicodeCharsReallocate * c_MaxUTF_8BytesPerUnicodeChar * c_EncodedCharsPerByte,
+                            ref destPos, prevInputPos);
+
+                        short numberOfBytes = (short)Encoding.UTF8.GetBytes(pStr + i, count, bytes,
+                            c_MaxUnicodeCharsReallocate * c_MaxUTF_8BytesPerUnicodeChar);
+
+                        // This is the only exception that built in UriParser can throw after a Uri ctor.
+                        // Should not happen unless the app tries to feed an invalid Unicode String
+                        if (numberOfBytes == 0)
+                            throw new FormatException(Environment.GetResourceString("Arg_FormatException"));
+
+                        i += (count - 1);
+
+                        for (count = 0; count < numberOfBytes; ++count)
+                            EscapeAsciiChar((char)bytes[count], dest, ref destPos);
+
+                        prevInputPos = i + 1;
+                    }
+                    else if (ch == '%' && rsvd == '%')
+                    {
+                        // Means we don't reEncode '%' but check for the possible escaped sequence
+                        dest = EnsureDestinationSize(pStr, dest, i, c_EncodedCharsPerByte,
+                            c_MaxAsciiCharsReallocate * c_EncodedCharsPerByte, ref destPos, prevInputPos);
+                        if (i + 2 < end && EscapedAscii(pStr[i + 1], pStr[i + 2]) != c_DummyChar)
+                        {
+                            // leave it escaped
+                            dest[destPos++] = '%';
+                            dest[destPos++] = pStr[i + 1];
+                            dest[destPos++] = pStr[i + 2];
+                            i += 2;
+                        }
+                        else
+                        {
+                            EscapeAsciiChar('%', dest, ref destPos);
+                        }
+                        prevInputPos = i + 1;
+                    }
+                    else if (ch == force1 || ch == force2)
+                    {
+                        dest = EnsureDestinationSize(pStr, dest, i, c_EncodedCharsPerByte,
+                            c_MaxAsciiCharsReallocate * c_EncodedCharsPerByte, ref destPos, prevInputPos);
+                        EscapeAsciiChar(ch, dest, ref destPos);
+                        prevInputPos = i + 1;
+                    }
+                    else if (ch != rsvd && (isUriString ? !IsReservedUnreservedOrHash(ch) : !IsUnreserved(ch)))
+                    {
+                        dest = EnsureDestinationSize(pStr, dest, i, c_EncodedCharsPerByte,
+                            c_MaxAsciiCharsReallocate * c_EncodedCharsPerByte, ref destPos, prevInputPos);
+                        EscapeAsciiChar(ch, dest, ref destPos);
+                        prevInputPos = i + 1;
+                    }
+                }
+
+                if (prevInputPos != i)
+                {
+                    // need to fill up the dest array ?
+                    if (prevInputPos != start || dest != null)
+                        dest = EnsureDestinationSize(pStr, dest, i, 0, 0, ref destPos, prevInputPos);
+                }
+            }
+
+            return dest;
+        }
+
+        //
+        // ensure destination array has enough space and contains all the needed input stuff
+        //
+        private unsafe static char[] EnsureDestinationSize(char* pStr, char[] dest, int currentInputPos,
+            short charsToAdd, short minReallocateChars, ref int destPos, int prevInputPos)
+        {
+            if ((object)dest == null || dest.Length < destPos + (currentInputPos - prevInputPos) + charsToAdd)
+            {
+                // allocating or reallocating array by ensuring enough space based on maxCharsToAdd.
+                char[] newresult = new char[destPos + (currentInputPos - prevInputPos) + minReallocateChars];
+
+                if ((object)dest != null && destPos != 0)
+                    Buffer.BlockCopy(dest, 0, newresult, 0, destPos << 1);
+                dest = newresult;
+            }
+
+            // ensuring we copied everything form the input string left before last escaping
+            while (prevInputPos != currentInputPos)
+                dest[destPos++] = pStr[prevInputPos++];
+            return dest;
+        }
+        
+        internal static void EscapeAsciiChar(char ch, char[] to, ref int pos)
+        {
+            to[pos++] = '%';
+            to[pos++] = s_hexUpperChars[(ch & 0xf0) >> 4];
+            to[pos++] = s_hexUpperChars[ch & 0xf];
+        }
+
+        internal static char EscapedAscii(char digit, char next)
+        {
+            if (!(((digit >= '0') && (digit <= '9'))
+                || ((digit >= 'A') && (digit <= 'F'))
+                || ((digit >= 'a') && (digit <= 'f'))))
+            {
+                return c_DummyChar;
+            }
+
+            int res = (digit <= '9')
+                ? ((int)digit - (int)'0')
+                : (((digit <= 'F')
+                ? ((int)digit - (int)'A')
+                : ((int)digit - (int)'a'))
+                   + 10);
+
+            if (!(((next >= '0') && (next <= '9'))
+                || ((next >= 'A') && (next <= 'F'))
+                || ((next >= 'a') && (next <= 'f'))))
+            {
+                return c_DummyChar;
+            }
+
+            return (char)((res << 4) + ((next <= '9')
+                    ? ((int)next - (int)'0')
+                    : (((next <= 'F')
+                        ? ((int)next - (int)'A')
+                        : ((int)next - (int)'a'))
+                       + 10)));
+        }       
+
+        private static unsafe bool IsReservedUnreservedOrHash(char c)
+        {
+            if (IsUnreserved(c))
+            {
+                return true;
+            }
+            return (RFC3986ReservedMarks.IndexOf(c) >= 0);
+        }
+
+        internal static unsafe bool IsUnreserved(char c)
+        {
+            if (IsAsciiLetterOrDigit(c))
+            {
+                return true;
+            }
+            return (RFC3986UnreservedMarks.IndexOf(c) >= 0);
+        }
+
+        //Only consider ASCII characters
+        internal static bool IsAsciiLetter(char character)
+        {
+            return (character >= 'a' && character <= 'z') ||
+                   (character >= 'A' && character <= 'Z');
+        }
+
+        internal static bool IsAsciiLetterOrDigit(char character)
+        {
+            return IsAsciiLetter(character) || (character >= '0' && character <= '9');
+        }
+        
+        private static readonly char[] s_hexUpperChars = {
+                                   '0', '1', '2', '3', '4', '5', '6', '7',
+                                   '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+        internal const char c_DummyChar = (char)0xFFFF;     //An Invalid Unicode character used as a dummy char passed into the parameter                                   
+        private const short c_MaxAsciiCharsReallocate = 40;
+        private const short c_MaxUnicodeCharsReallocate = 40;
+        private const short c_MaxUTF_8BytesPerUnicodeChar = 4;
+        private const short c_EncodedCharsPerByte = 3;     
+        private const string RFC3986ReservedMarks = @":/?#[]@!$&'()*+,;=";
+        private const string RFC3986UnreservedMarks = @"-._~";
     }
 }