summaryrefslogtreecommitdiff
path: root/src/mscorlib
diff options
context:
space:
mode:
authorSepideh Khoshnood <sekho@microsoft.com>2017-05-11 11:45:06 -0700
committerGitHub <noreply@github.com>2017-05-11 11:45:06 -0700
commite52d80fa5b53fea51d18f03e441cf0c2fff58419 (patch)
tree7ddc20d69116c214a077bb0a2ced936833c6de94 /src/mscorlib
parent27f4b80d1e0935bcd04b1a0024e824984b407928 (diff)
downloadcoreclr-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')
-rw-r--r--src/mscorlib/System.Private.CoreLib.csproj8
-rw-r--r--src/mscorlib/src/System/Reflection/AssemblyName.cs5
-rw-r--r--src/mscorlib/src/System/Reflection/Runtime/Assemblies/AssemblyNameHelpers.cs174
-rw-r--r--src/mscorlib/src/System/Reflection/Runtime/Assemblies/AssemblyNameLexer.cs140
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;
+ }
+}
+