diff options
author | Sepideh Khoshnood <sekho@microsoft.com> | 2017-05-11 11:45:06 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-05-11 11:45:06 -0700 |
commit | e52d80fa5b53fea51d18f03e441cf0c2fff58419 (patch) | |
tree | 7ddc20d69116c214a077bb0a2ced936833c6de94 /src/mscorlib | |
parent | 27f4b80d1e0935bcd04b1a0024e824984b407928 (diff) | |
download | coreclr-e52d80fa5b53fea51d18f03e441cf0c2fff58419.tar.gz coreclr-e52d80fa5b53fea51d18f03e441cf0c2fff58419.tar.bz2 coreclr-e52d80fa5b53fea51d18f03e441cf0c2fff58419.zip |
Fix AssemblyName version (#11505)
* Fix AssemblyName.FullName
* Use canonicalized version instead of Version.ToString
Diffstat (limited to 'src/mscorlib')
4 files changed, 323 insertions, 4 deletions
diff --git a/src/mscorlib/System.Private.CoreLib.csproj b/src/mscorlib/System.Private.CoreLib.csproj index ff4aa31eb7..4255499d50 100644 --- a/src/mscorlib/System.Private.CoreLib.csproj +++ b/src/mscorlib/System.Private.CoreLib.csproj @@ -86,11 +86,9 @@ <PropertyGroup Condition="'$(OsEnvironment)' == 'Unix'"> <DebugType>portable</DebugType> </PropertyGroup> - <PropertyGroup Condition="'$(TargetsOSX)' == 'true'"> <DefineConstants>PLATFORM_OSX;$(DefineConstants)</DefineConstants> </PropertyGroup> - <!-- Assembly attributes --> <PropertyGroup> <AssemblyName>System.Private.CoreLib</AssemblyName> @@ -457,6 +455,10 @@ <Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\XXXOnTypeBuilderInstantiation.cs" /> </ItemGroup> <ItemGroup> + <Compile Include="$(BclSourcesRoot)\System\Reflection\Runtime\Assemblies\AssemblyNameHelpers.cs" /> + <Compile Include="$(BclSourcesRoot)\System\Reflection\Runtime\Assemblies\AssemblyNameLexer.cs" /> + </ItemGroup> + <ItemGroup> <Compile Include="$(BclSourcesRoot)\System\Reflection\Metadata\AssemblyExtensions.cs" /> </ItemGroup> <ItemGroup> @@ -731,4 +733,4 @@ <Win32Resource Condition="'$(GenerateNativeVersionInfo)'=='true'">$(IntermediateOutputPath)\System.Private.CoreLib.res</Win32Resource> </PropertyGroup> <Import Project="GenerateCompilerResponseFile.targets" /> -</Project> +</Project>
\ No newline at end of file diff --git a/src/mscorlib/src/System/Reflection/AssemblyName.cs b/src/mscorlib/src/System/Reflection/AssemblyName.cs index 80fdf5d162..6fa60df238 100644 --- a/src/mscorlib/src/System/Reflection/AssemblyName.cs +++ b/src/mscorlib/src/System/Reflection/AssemblyName.cs @@ -19,6 +19,7 @@ namespace System.Reflection using System; using System.IO; using System.Configuration.Assemblies; + using System.Reflection.Runtime.Assemblies; using System.Runtime.CompilerServices; using CultureInfo = System.Globalization.CultureInfo; using System.Runtime.Serialization; @@ -279,7 +280,9 @@ namespace System.Reflection { get { - return nToString(); + if (this.Name == null) + return string.Empty; + return AssemblyNameHelpers.ComputeDisplayName(this); } } diff --git a/src/mscorlib/src/System/Reflection/Runtime/Assemblies/AssemblyNameHelpers.cs b/src/mscorlib/src/System/Reflection/Runtime/Assemblies/AssemblyNameHelpers.cs new file mode 100644 index 0000000000..cd1be684eb --- /dev/null +++ b/src/mscorlib/src/System/Reflection/Runtime/Assemblies/AssemblyNameHelpers.cs @@ -0,0 +1,174 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** + Type: AssemblyNameHelpers +** +==============================================================*/ + +using System; +using System.Globalization; +using System.IO; +using System.Text; +using System.Collections.Generic; + +namespace System.Reflection.Runtime.Assemblies +{ + internal static class AssemblyNameHelpers + { + private const int PUBLIC_KEY_TOKEN_LEN = 8; + + public static String ComputeDisplayName(AssemblyName a) + { + if (a.Name == String.Empty) + throw new FileLoadException(); + + StringBuilder sb = new StringBuilder(); + if (a.Name != null) + { + sb.AppendQuoted(a.Name); + } + + if (a.Version != null) + { + Version canonicalizedVersion = a.Version.CanonicalizeVersion(); + if (canonicalizedVersion.Major != ushort.MaxValue) + { + sb.Append(", Version="); + sb.Append(canonicalizedVersion.Major); + + if(canonicalizedVersion.Minor != ushort.MaxValue) + { + sb.Append('.'); + sb.Append(canonicalizedVersion.Minor); + + if(canonicalizedVersion.Build != ushort.MaxValue) + { + sb.Append('.'); + sb.Append(canonicalizedVersion.Build); + + if(canonicalizedVersion.Revision != ushort.MaxValue) + { + sb.Append('.'); + sb.Append(canonicalizedVersion.Revision); + } + } + } + } + } + + String cultureName = a.CultureName; + if (cultureName != null) + { + if (cultureName == String.Empty) + cultureName = "neutral"; + sb.Append(", Culture="); + sb.AppendQuoted(cultureName); + } + + byte[] pkt = a.GetPublicKeyToken(); + if (pkt != null) + { + if (pkt.Length > PUBLIC_KEY_TOKEN_LEN) + throw new ArgumentException(); + + sb.Append(", PublicKeyToken="); + if (pkt.Length == 0) + sb.Append("null"); + else + { + foreach (byte b in pkt) + { + sb.Append(b.ToString("x2", CultureInfo.InvariantCulture)); + } + } + } + + if (0 != (a.Flags & AssemblyNameFlags.Retargetable)) + sb.Append(", Retargetable=Yes"); + + AssemblyContentType contentType = a.ContentType; + if (contentType == AssemblyContentType.WindowsRuntime) + sb.Append(", ContentType=WindowsRuntime"); + + // NOTE: By design (desktop compat) AssemblyName.FullName and ToString() do not include ProcessorArchitecture. + + return sb.ToString(); + } + + private static void AppendQuoted(this StringBuilder sb, String s) + { + bool needsQuoting = false; + const char quoteChar = '\"'; + + //@todo: App-compat: You can use double or single quotes to quote a name, and Fusion (or rather the IdentityAuthority) picks one + // by some algorithm. Rather than guess at it, I'll just use double-quote consistently. + if (s != s.Trim() || s.Contains("\"") || s.Contains("\'")) + needsQuoting = true; + + if (needsQuoting) + sb.Append(quoteChar); + + for (int i = 0; i < s.Length; i++) + { + bool addedEscape = false; + foreach (KeyValuePair<char, String> kv in AssemblyNameLexer.EscapeSequences) + { + String escapeReplacement = kv.Value; + if (!(s[i] == escapeReplacement[0])) + continue; + if ((s.Length - i) < escapeReplacement.Length) + continue; + String prefix = s.Substring(i, escapeReplacement.Length); + if (prefix == escapeReplacement) + { + sb.Append('\\'); + sb.Append(kv.Key); + addedEscape = true; + } + } + + if (!addedEscape) + sb.Append(s[i]); + } + + if (needsQuoting) + sb.Append(quoteChar); + } + + public static Version CanonicalizeVersion(this Version version) + { + ushort major = (ushort)version.Major; + ushort minor = (ushort)version.Minor; + ushort build = (ushort)version.Build; + ushort revision = (ushort)version.Revision; + + if (major == version.Major && minor == version.Minor && build == version.Build && revision == version.Revision) + return version; + + return new Version(major, minor, build, revision); + } + + internal static AssemblyContentType ExtractAssemblyContentType(this AssemblyNameFlags flags) + { + return (AssemblyContentType)((((int)flags) >> 9) & 0x7); + } + + internal static ProcessorArchitecture ExtractProcessorArchitecture(this AssemblyNameFlags flags) + { + return (ProcessorArchitecture)((((int)flags) >> 4) & 0x7); + } + + public static AssemblyNameFlags ExtractAssemblyNameFlags(this AssemblyNameFlags combinedFlags) + { + return combinedFlags & unchecked((AssemblyNameFlags)0xFFFFF10F); + } + + internal static AssemblyNameFlags CombineAssemblyNameFlags(AssemblyNameFlags flags, AssemblyContentType contentType, ProcessorArchitecture processorArchitecture) + { + return (AssemblyNameFlags)(((int)flags) | (((int)contentType) << 9) | ((int)processorArchitecture << 4)); + } + } +}
\ No newline at end of file diff --git a/src/mscorlib/src/System/Reflection/Runtime/Assemblies/AssemblyNameLexer.cs b/src/mscorlib/src/System/Reflection/Runtime/Assemblies/AssemblyNameLexer.cs new file mode 100644 index 0000000000..cac187ba3a --- /dev/null +++ b/src/mscorlib/src/System/Reflection/Runtime/Assemblies/AssemblyNameLexer.cs @@ -0,0 +1,140 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO; +using System.Text; +using System.Globalization; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace System.Reflection.Runtime.Assemblies +{ + // + // A simple lexer for assembly display names. + // + internal struct AssemblyNameLexer + { + internal AssemblyNameLexer(String s) + { + // Convert string to char[] with NUL terminator. (An actual NUL terminator in the input string will be treated + // as an actual end of string: this is compatible with desktop behavior.) + char[] chars = new char[s.Length + 1]; + s.CopyTo(0, chars, 0, s.Length); + _chars = chars; + _index = 0; + } + + // + // Return the next token in assembly name. If you expect the result to be DisplayNameToken.String, + // use GetNext(out String) instead. + // + internal Token GetNext() + { + String ignore; + return GetNext(out ignore); + } + + // + // Return the next token in assembly name. If the result is DisplayNameToken.String, + // sets "tokenString" to the tokenized string. + // + internal Token GetNext(out String tokenString) + { + tokenString = null; + while (Char.IsWhiteSpace(_chars[_index])) + _index++; + + char c = _chars[_index++]; + if (c == 0) + return Token.End; + if (c == ',') + return Token.Comma; + if (c == '=') + return Token.Equals; + + StringBuilder sb = new StringBuilder(); + + char quoteChar = (char)0; + if (c == '\'' || c == '\"') + { + quoteChar = c; + c = _chars[_index++]; + } + + for (;;) + { + if (c == 0) + { + _index--; + break; // Terminate: End of string (desktop compat: if string was quoted, permitted to terminate without end-quote.) + } + + if (quoteChar != 0 && c == quoteChar) + break; // Terminate: Found closing quote of quoted string. + + if (quoteChar == 0 && (c == ',' || c == '=')) + { + _index--; + break; // Terminate: Found start of a new ',' or '=' token. + } + + if (quoteChar == 0 && (c == '\'' || c == '\"')) + throw new FileLoadException(); // Desktop compat: Unescaped quote illegal unless entire string is quoted. + + if (c == '\\') + { + c = _chars[_index++]; + bool matched = false; + foreach (KeyValuePair<char, String> kv in EscapeSequences) + { + if (c == kv.Key) + { + matched = true; + sb.Append(kv.Value); + break; + } + } + if (!matched) + throw new FileLoadException(); // Unrecognized escape + } + else + { + sb.Append(c); + } + + c = _chars[_index++]; + } + + tokenString = sb.ToString(); + if (quoteChar == 0) + tokenString = tokenString.Trim(); // Unless quoted, whitespace at beginning or end doesn't count. + return Token.String; + } + + internal static KeyValuePair<char, String>[] EscapeSequences = + { + new KeyValuePair<char, String>('\\', "\\"), + new KeyValuePair<char, String>(',', ","), + new KeyValuePair<char, String>('=', "="), + new KeyValuePair<char, String>('\'', "'"), + new KeyValuePair<char, String>('\"', "\""), + new KeyValuePair<char, String>('n', Environment.NewLine), + new KeyValuePair<char, String>('t', "\t"), + }; + + // Token categories for display name lexer. + internal enum Token + { + Equals = 1, + Comma = 2, + String = 3, + End = 4, + } + + private readonly char[] _chars; + private int _index; + } +} + |