Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/linker.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMateo Torres-Ruiz <mateoatr@users.noreply.github.com>2020-09-16 16:23:06 +0300
committerGitHub <noreply@github.com>2020-09-16 16:23:06 +0300
commitff542781fd221ff5d029d4a189dfbfeebb74cffe (patch)
treed78f79260c48bb4d48fe70d0ac5fe1fad6259042 /external
parent78e35797f5b1b0b1df0aa25111f6574291d8deb0 (diff)
Port .NET Native type name parser (#1472)
* Add type parser * PR feedback * Fix mono build * Move corert code to external * Match filepaths
Diffstat (limited to 'external')
-rw-r--r--external/corert/README.md1
-rw-r--r--external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameFormatter.cs156
-rw-r--r--external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameHelpers.cs42
-rw-r--r--external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameLexer.cs124
-rw-r--r--external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameParser.cs216
-rw-r--r--external/corert/src/System.Private.CoreLib/shared/System/Reflection/RuntimeAssemblyName.cs123
-rw-r--r--external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeLexer.cs238
-rw-r--r--external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeName.cs219
-rw-r--r--external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeParser.cs225
9 files changed, 1344 insertions, 0 deletions
diff --git a/external/corert/README.md b/external/corert/README.md
new file mode 100644
index 000000000..babf7d8e2
--- /dev/null
+++ b/external/corert/README.md
@@ -0,0 +1 @@
+The code in this folder was adapted from dotnet/corert commit c8bfca5f4554badfb89b80d2319769f83512bf62 \ No newline at end of file
diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameFormatter.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameFormatter.cs
new file mode 100644
index 000000000..821a8d3c8
--- /dev/null
+++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameFormatter.cs
@@ -0,0 +1,156 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.IO;
+using System.Text;
+using System.Globalization;
+using System.Collections.Generic;
+
+namespace System.Reflection
+{
+ internal static class AssemblyNameFormatter
+ {
+ public static string ComputeDisplayName(RuntimeAssemblyName a)
+ {
+ const int PUBLIC_KEY_TOKEN_LEN = 8;
+
+ 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.PublicKeyOrToken;
+ 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.Flags.ExtractAssemblyContentType();
+ 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 = '\"';
+
+ // 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, we use double quotes 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 EscapeSequences)
+ {
+ string escapeReplacement = kv.Value;
+ if (!(s[i] == escapeReplacement[0]))
+ continue;
+ if ((s.Length - i) < escapeReplacement.Length)
+ continue;
+ if (s.Substring(i, escapeReplacement.Length).Equals(escapeReplacement))
+ {
+ sb.Append('\\');
+ sb.Append(kv.Key);
+ addedEscape = true;
+ }
+ }
+
+ if (!addedEscape)
+ sb.Append(s[i]);
+ }
+
+ if (needsQuoting)
+ sb.Append(quoteChar);
+ }
+
+ private 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);
+ }
+
+ public 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"),
+ };
+ }
+} \ No newline at end of file
diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameHelpers.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameHelpers.cs
new file mode 100644
index 000000000..9b9c1a702
--- /dev/null
+++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameHelpers.cs
@@ -0,0 +1,42 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Globalization;
+using System.IO;
+using System.Text;
+using System.Collections.Generic;
+
+namespace System.Reflection
+{
+ public static partial class AssemblyNameHelpers
+ {
+
+
+ //
+ // These helpers convert between the combined flags+contentType+processorArchitecture value and the separated parts.
+ //
+ // Since these are only for trusted callers, they do NOT check for out of bound bits.
+ //
+
+ 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/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameLexer.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameLexer.cs
new file mode 100644
index 000000000..5ef3d595a
--- /dev/null
+++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameLexer.cs
@@ -0,0 +1,124 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.IO;
+using System.Text;
+using System.Collections.Generic;
+
+namespace System.Reflection
+{
+ //
+ // 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 AssemblyNameFormatter.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;
+ }
+
+ // Token categories for display name lexer.
+ internal enum Token
+ {
+ Equals = 1,
+ Comma = 2,
+ String = 3,
+ End = 4,
+ }
+
+ private readonly char[] _chars;
+ private int _index;
+ }
+} \ No newline at end of file
diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameParser.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameParser.cs
new file mode 100644
index 000000000..fdf5b8f06
--- /dev/null
+++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameParser.cs
@@ -0,0 +1,216 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.IO;
+using System.Diagnostics;
+using System.Globalization;
+using System.Collections.Generic;
+
+namespace System.Reflection
+{
+ //
+ // Parses an assembly name.
+ //
+ internal static class AssemblyNameParser
+ {
+ internal static RuntimeAssemblyName Parse(string s)
+ {
+ Debug.Assert(s != null);
+
+ int indexOfNul = s.IndexOf((char)0);
+ if (indexOfNul != -1)
+ s = s.Substring(0, indexOfNul);
+ if (s.Length == 0)
+ throw new ArgumentException();
+
+ AssemblyNameLexer lexer = new AssemblyNameLexer(s);
+
+ // Name must come first.
+ string name;
+ AssemblyNameLexer.Token token = lexer.GetNext(out name);
+ if (token != AssemblyNameLexer.Token.String)
+ throw new FileLoadException();
+
+ if (name == string.Empty || name.IndexOfAny(s_illegalCharactersInSimpleName) != -1)
+ throw new FileLoadException();
+
+ Version version = null;
+ string cultureName = null;
+ byte[] pkt = null;
+ AssemblyNameFlags flags = 0;
+
+ List<string> alreadySeen = new List<string>();
+ token = lexer.GetNext();
+ while (token != AssemblyNameLexer.Token.End)
+ {
+ if (token != AssemblyNameLexer.Token.Comma)
+ throw new FileLoadException();
+ string attributeName;
+ token = lexer.GetNext(out attributeName);
+ if (token != AssemblyNameLexer.Token.String)
+ throw new FileLoadException();
+ token = lexer.GetNext();
+
+ // Compat note: Inside AppX apps, the desktop CLR's AssemblyName parser skips past any elements that don't follow the "<Something>=<Something>" pattern.
+ // (when running classic Windows apps, such an illegal construction throws an exception as expected.)
+ // Naturally, at least one app unwittingly takes advantage of this.
+ if (token == AssemblyNameLexer.Token.Comma || token == AssemblyNameLexer.Token.End)
+ continue;
+
+ if (token != AssemblyNameLexer.Token.Equals)
+ throw new FileLoadException();
+ string attributeValue;
+ token = lexer.GetNext(out attributeValue);
+ if (token != AssemblyNameLexer.Token.String)
+ throw new FileLoadException();
+
+ if (attributeName == string.Empty)
+ throw new FileLoadException();
+
+ for (int i = 0; i < alreadySeen.Count; i++)
+ {
+ if (alreadySeen[i].Equals(attributeName, StringComparison.OrdinalIgnoreCase))
+ throw new FileLoadException(); // Cannot specify the same attribute twice.
+ }
+ alreadySeen.Add(attributeName);
+ if (attributeName.Equals("Version", StringComparison.OrdinalIgnoreCase))
+ {
+ version = ParseVersion(attributeValue);
+ }
+
+ if (attributeName.Equals("Culture", StringComparison.OrdinalIgnoreCase))
+ {
+ cultureName = ParseCulture(attributeValue);
+ }
+
+ if (attributeName.Equals("PublicKeyToken", StringComparison.OrdinalIgnoreCase))
+ {
+ pkt = ParsePKT(attributeValue);
+ }
+
+ if (attributeName.Equals("ProcessorArchitecture", StringComparison.OrdinalIgnoreCase))
+ {
+ flags |= (AssemblyNameFlags)(((int)ParseProcessorArchitecture(attributeValue)) << 4);
+ }
+
+ if (attributeName.Equals("Retargetable", StringComparison.OrdinalIgnoreCase))
+ {
+ if (attributeValue.Equals("Yes", StringComparison.OrdinalIgnoreCase))
+ flags |= AssemblyNameFlags.Retargetable;
+ else if (attributeValue.Equals("No", StringComparison.OrdinalIgnoreCase))
+ {
+ // nothing to do
+ }
+ else
+ throw new FileLoadException();
+ }
+
+ if (attributeName.Equals("ContentType", StringComparison.OrdinalIgnoreCase))
+ {
+ if (attributeValue.Equals("WindowsRuntime", StringComparison.OrdinalIgnoreCase))
+ flags |= (AssemblyNameFlags)(((int)AssemblyContentType.WindowsRuntime) << 9);
+ else
+ throw new FileLoadException();
+ }
+
+ // Desktop compat: If we got here, the attribute name is unknown to us. Ignore it (as long it's not duplicated.)
+ token = lexer.GetNext();
+ }
+ return new RuntimeAssemblyName(name, version, cultureName, flags, pkt);
+ }
+
+ private static Version ParseVersion(string attributeValue)
+ {
+ string[] parts = attributeValue.Split('.');
+ if (parts.Length > 4)
+ throw new FileLoadException();
+ ushort[] versionNumbers = new ushort[4];
+ for (int i = 0; i < versionNumbers.Length; i++)
+ {
+ if (i >= parts.Length)
+ versionNumbers[i] = ushort.MaxValue;
+ else
+ {
+ // Desktop compat: TryParse is a little more forgiving than Fusion.
+ for (int j = 0; j < parts[i].Length; j++)
+ {
+ if (!char.IsDigit(parts[i][j]))
+ throw new FileLoadException();
+ }
+ if (!(ushort.TryParse(parts[i], out versionNumbers[i])))
+ {
+ throw new FileLoadException();
+ }
+ }
+ }
+
+ if (versionNumbers[0] == ushort.MaxValue || versionNumbers[1] == ushort.MaxValue)
+ throw new FileLoadException();
+ if (versionNumbers[2] == ushort.MaxValue)
+ return new Version(versionNumbers[0], versionNumbers[1]);
+ if (versionNumbers[3] == ushort.MaxValue)
+ return new Version(versionNumbers[0], versionNumbers[1], versionNumbers[2]);
+ return new Version(versionNumbers[0], versionNumbers[1], versionNumbers[2], versionNumbers[3]);
+ }
+
+ private static string ParseCulture(string attributeValue)
+ {
+ if (attributeValue.Equals("Neutral", StringComparison.OrdinalIgnoreCase))
+ {
+ return "";
+ }
+ else
+ {
+ CultureInfo culture = CultureInfo.GetCultureInfo(attributeValue); // Force a CultureNotFoundException if not a valid culture.
+ return culture.Name;
+ }
+ }
+
+ private static byte[] ParsePKT(string attributeValue)
+ {
+ if (attributeValue.Equals("null", StringComparison.OrdinalIgnoreCase) || attributeValue == string.Empty)
+ return Array.Empty<byte>();
+
+ if (attributeValue.Length != 8 * 2)
+ throw new FileLoadException();
+
+ byte[] pkt = new byte[8];
+ int srcIndex = 0;
+ for (int i = 0; i < 8; i++)
+ {
+ char hi = attributeValue[srcIndex++];
+ char lo = attributeValue[srcIndex++];
+ pkt[i] = (byte)((ParseHexNybble(hi) << 4) | ParseHexNybble(lo));
+ }
+ return pkt;
+ }
+
+ private static ProcessorArchitecture ParseProcessorArchitecture(string attributeValue)
+ {
+ if (attributeValue.Equals("msil", StringComparison.OrdinalIgnoreCase))
+ return ProcessorArchitecture.MSIL;
+ if (attributeValue.Equals("x86", StringComparison.OrdinalIgnoreCase))
+ return ProcessorArchitecture.X86;
+ if (attributeValue.Equals("ia64", StringComparison.OrdinalIgnoreCase))
+ return ProcessorArchitecture.IA64;
+ if (attributeValue.Equals("amd64", StringComparison.OrdinalIgnoreCase))
+ return ProcessorArchitecture.Amd64;
+ if (attributeValue.Equals("arm", StringComparison.OrdinalIgnoreCase))
+ return ProcessorArchitecture.Arm;
+ throw new FileLoadException();
+ }
+
+ private static byte ParseHexNybble(char c)
+ {
+ if (c >= '0' && c <= '9')
+ return (byte)(c - '0');
+ if (c >= 'a' && c <= 'f')
+ return (byte)(c - 'a' + 10);
+ if (c >= 'A' && c <= 'F')
+ return (byte)(c - 'A' + 10);
+ throw new FileLoadException();
+ }
+
+ private static readonly char[] s_illegalCharactersInSimpleName = { '/', '\\', ':' };
+ }
+} \ No newline at end of file
diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/RuntimeAssemblyName.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/RuntimeAssemblyName.cs
new file mode 100644
index 000000000..561e9382f
--- /dev/null
+++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/RuntimeAssemblyName.cs
@@ -0,0 +1,123 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+
+namespace System.Reflection
+{
+ //
+ // This is a private assembly name abstraction that's more suitable for use as keys in our caches.
+ //
+ // - Immutable, unlike the public AssemblyName
+ // - Has a useful Equals() override, unlike the public AssemblyName.
+ //
+ // We use this as our internal interchange type and only convert to and from the public AssemblyName class at public boundaries.
+ //
+ public sealed class RuntimeAssemblyName : IEquatable<RuntimeAssemblyName>
+ {
+ public RuntimeAssemblyName(string name, Version version, string cultureName, AssemblyNameFlags flags, byte[] publicKeyOrToken)
+ {
+ Debug.Assert(name != null);
+ this.Name = name;
+
+ // Optional version.
+ this.Version = version;
+
+ // Optional culture name.
+ this.CultureName = cultureName;
+
+ // Optional flags (this is actually an OR of the classic flags and the ContentType.)
+ this.Flags = flags;
+
+ // Optional public key (if Flags.PublicKey == true) or public key token.
+ this.PublicKeyOrToken = publicKeyOrToken;
+ }
+
+ // Simple name.
+ public string Name { get; }
+
+ // Optional version.
+ public Version Version { get; }
+
+ // Optional culture name.
+ public string CultureName { get; }
+
+ // Optional flags (this is actually an OR of the classic flags and the ContentType.)
+ public AssemblyNameFlags Flags { get; }
+
+ // Optional public key (if Flags.PublicKey == true) or public key token.
+ public byte[] PublicKeyOrToken { get; }
+
+ // Equality - this compares every bit of data in the RuntimeAssemblyName which is acceptable for use as keys in a cache
+ // where semantic duplication is permissible. This method is *not* meant to define ref->def binding rules or
+ // assembly binding unification rules.
+ public bool Equals(RuntimeAssemblyName other)
+ {
+ if (other == null)
+ return false;
+ if (!this.Name.Equals(other.Name))
+ return false;
+ if (this.Version == null)
+ {
+ if (other.Version != null)
+ return false;
+ }
+ else
+ {
+ if (!this.Version.Equals(other.Version))
+ return false;
+ }
+ if (!string.Equals(this.CultureName, other.CultureName))
+ return false;
+ if (this.Flags != other.Flags)
+ return false;
+
+ byte[] thisPK = this.PublicKeyOrToken;
+ byte[] otherPK = other.PublicKeyOrToken;
+ if (thisPK == null)
+ {
+ if (otherPK != null)
+ return false;
+ }
+ else if (otherPK == null)
+ {
+ return false;
+ }
+ else if (thisPK.Length != otherPK.Length)
+ {
+ return false;
+ }
+ else
+ {
+ for (int i = 0; i < thisPK.Length; i++)
+ {
+ if (thisPK[i] != otherPK[i])
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public sealed override bool Equals(object obj)
+ {
+ RuntimeAssemblyName other = obj as RuntimeAssemblyName;
+ if (other == null)
+ return false;
+ return Equals(other);
+ }
+
+ public sealed override int GetHashCode()
+ {
+ return this.Name.GetHashCode();
+ }
+
+ public string FullName
+ {
+ get
+ {
+ return AssemblyNameFormatter.ComputeDisplayName(this);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeLexer.cs b/external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeLexer.cs
new file mode 100644
index 000000000..4a2429721
--- /dev/null
+++ b/external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeLexer.cs
@@ -0,0 +1,238 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+
+namespace System.Reflection.Runtime.TypeParsing
+{
+ //
+ // String tokenizer for typenames passed to the GetType() api's.
+ //
+ internal sealed class TypeLexer
+ {
+ public TypeLexer(String s)
+ {
+ // Turn the string into a char array with a NUL terminator.
+ char[] chars = new char[s.Length + 1];
+ s.CopyTo(0, chars, 0, s.Length);
+ _chars = chars;
+ _index = 0;
+ }
+
+ public TokenType Peek
+ {
+ get
+ {
+ SkipWhiteSpace();
+ char c = _chars[_index];
+ return CharToToken(c);
+ }
+ }
+
+ public TokenType PeekSecond
+ {
+ get
+ {
+ SkipWhiteSpace();
+ int index = _index + 1;
+ while (Char.IsWhiteSpace(_chars[index]))
+ index++;
+ char c = _chars[index];
+ return CharToToken(c);
+ }
+ }
+
+
+ public void Skip()
+ {
+ Debug.Assert(_index != _chars.Length);
+ SkipWhiteSpace();
+ _index++;
+ }
+
+ // Return the next token and skip index past it unless already at end of string
+ // or the token is not a reserved token.
+ public TokenType GetNextToken()
+ {
+ TokenType tokenType = Peek;
+ if (tokenType == TokenType.End || tokenType == TokenType.Other)
+ return tokenType;
+ Skip();
+ return tokenType;
+ }
+
+ //
+ // Lex the next segment as part of a type name. (Do not use for assembly names.)
+ //
+ // Note that unescaped "."'s do NOT terminate the identifier, but unescaped "+"'s do.
+ //
+ // Terminated by the first non-escaped reserved character ('[', ']', '+', '&', '*' or ',')
+ //
+ public String GetNextIdentifier()
+ {
+ SkipWhiteSpace();
+
+ int src = _index;
+ char[] buffer = new char[_chars.Length];
+ int dst = 0;
+ for (; ; )
+ {
+ char c = _chars[src];
+ TokenType token = CharToToken(c);
+ if (token != TokenType.Other)
+ break;
+ src++;
+ if (c == '\\')
+ {
+ c = _chars[src];
+ if (c != NUL)
+ src++;
+ if (c == NUL || CharToToken(c) == TokenType.Other)
+ {
+ // If we got here, a backslash was used to escape a character that is not legal to escape inside a type name.
+ //
+ // Common sense would dictate throwing an ArgumentException but that's not what the desktop CLR does.
+ // The desktop CLR treats this case by returning FALSE from TypeName::TypeNameParser::GetIdentifier().
+ // Unfortunately, no one checks this return result. Instead, the CLR keeps parsing (unfortunately, the lexer
+ // was left in some strange state by the previous failure but typically, this goes unnoticed) and eventually, tries to resolve
+ // a Type whose name is the empty string. When it can't resolve that type, the CLR throws a TypeLoadException()
+ // complaining about be unable to find a type with the empty name.
+ //
+ // To emulate this accidental behavior, we'll throw a special exception that's caught by the TypeParser.
+ //
+ throw new IllegalEscapeSequenceException();
+ }
+ }
+ buffer[dst++] = c;
+ }
+
+ _index = src;
+ return new String(buffer, 0, dst);
+ }
+
+ //
+ // Lex the next segment as the assembly name at the end of an assembly-qualified type name. (Do not use for
+ // assembly names embedded inside generic type arguments.)
+ //
+ // Terminated by NUL. There are no escape characters defined by the typename lexer (however, AssemblyName
+ // does have its own escape rules.)
+ //
+ public RuntimeAssemblyName GetNextAssemblyName()
+ {
+ SkipWhiteSpace();
+
+ int src = _index;
+ char[] buffer = new char[_chars.Length];
+ int dst = 0;
+ for (; ; )
+ {
+ char c = _chars[src];
+ if (c == NUL)
+ break;
+ src++;
+ buffer[dst++] = c;
+ }
+ _index = src;
+ String fullName = new String(buffer, 0, dst);
+ return AssemblyNameParser.Parse(fullName);
+ }
+
+ //
+ // Lex the next segment as an assembly name embedded inside a generic argument type.
+ //
+ // Terminated by an unescaped ']'.
+ //
+ public RuntimeAssemblyName GetNextEmbeddedAssemblyName()
+ {
+ SkipWhiteSpace();
+
+ int src = _index;
+ char[] buffer = new char[_chars.Length];
+ int dst = 0;
+ for (; ; )
+ {
+ char c = _chars[src];
+ if (c == NUL)
+ throw new ArgumentException();
+ if (c == ']')
+ break;
+ src++;
+
+ // Backslash can be used to escape a ']' - any other backslash character is left alone (along with the backslash)
+ // for the AssemblyName parser to handle.
+ if (c == '\\' && _chars[src] == ']')
+ {
+ c = _chars[src++];
+ }
+ buffer[dst++] = c;
+ }
+ _index = src;
+ String fullName = new String(buffer, 0, dst);
+ return AssemblyNameParser.Parse(fullName);
+ }
+
+ //
+ // Classify a character as a TokenType. (Fortunately, all tokens in typename strings other than identifiers are single-character tokens.)
+ //
+ private static TokenType CharToToken(char c)
+ {
+ switch (c)
+ {
+ case NUL:
+ return TokenType.End;
+ case '[':
+ return TokenType.OpenSqBracket;
+ case ']':
+ return TokenType.CloseSqBracket;
+ case ',':
+ return TokenType.Comma;
+ case '+':
+ return TokenType.Plus;
+ case '*':
+ return TokenType.Asterisk;
+ case '&':
+ return TokenType.Ampersand;
+ default:
+ return TokenType.Other;
+ }
+ }
+
+ //
+ // The desktop typename parser has a strange attitude towards whitespace. It throws away whitespace between punctuation tokens and whitespace
+ // preceeding identifiers or assembly names (and this cannot be escaped away). But whitespace between the end of an identifier
+ // and the punctuation that ends it is *not* ignored.
+ //
+ // In other words, GetType(" Foo") searches for "Foo" but GetType("Foo ") searches for "Foo ".
+ //
+ // Whitespace between the end of an assembly name and the punction mark that ends it is also not ignored by this parser,
+ // but this is irrelevant since the assembly name is then turned over to AssemblyName for parsing, which *does* ignore trailing whitespace.
+ //
+ private void SkipWhiteSpace()
+ {
+ while (Char.IsWhiteSpace(_chars[_index]))
+ _index++;
+ }
+
+
+ private int _index;
+ private readonly char[] _chars;
+ private const char NUL = (char)0;
+
+
+ public sealed class IllegalEscapeSequenceException : Exception
+ {
+ }
+ }
+
+ internal enum TokenType
+ {
+ End = 0, //At end of string
+ OpenSqBracket = 1, //'['
+ CloseSqBracket = 2, //']'
+ Comma = 3, //','
+ Plus = 4, //'+'
+ Asterisk = 5, //'*'
+ Ampersand = 6, //'&'
+ Other = 7, //Type identifier, AssemblyName or embedded AssemblyName.
+ }
+} \ No newline at end of file
diff --git a/external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeName.cs b/external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeName.cs
new file mode 100644
index 000000000..81403cc4f
--- /dev/null
+++ b/external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeName.cs
@@ -0,0 +1,219 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+using System.Collections.Generic;
+
+namespace System.Reflection.Runtime.TypeParsing
+{
+ //
+ // The TypeName class is the base class for a family of types that represent the nodes in a parse tree for
+ // assembly-qualified type names.
+ //
+ public abstract class TypeName
+ {
+ public abstract override string ToString();
+ }
+
+ //
+ // Represents a parse of a type name optionally qualified by an assembly name. If present, the assembly name follows
+ // a comma following the type name.
+ //
+ public sealed class AssemblyQualifiedTypeName : TypeName
+ {
+ public AssemblyQualifiedTypeName(NonQualifiedTypeName typeName, RuntimeAssemblyName assemblyName)
+ {
+ Debug.Assert(typeName != null);
+ TypeName = typeName;
+ AssemblyName = assemblyName;
+ }
+
+ public sealed override string ToString()
+ {
+ return TypeName.ToString() + ((AssemblyName == null) ? "" : ", " + AssemblyName.FullName);
+ }
+
+ public RuntimeAssemblyName AssemblyName { get; }
+ public NonQualifiedTypeName TypeName { get; }
+ }
+
+ //
+ // Base class for all non-assembly-qualified type names.
+ //
+ public abstract class NonQualifiedTypeName : TypeName
+ {
+ }
+
+ //
+ // Base class for namespace or nested type.
+ //
+ internal abstract class NamedTypeName : NonQualifiedTypeName
+ {
+ }
+
+ //
+ // Non-nested named type. The full name is the namespace-qualified name. For example, the FullName for
+ // System.Collections.Generic.IList<> is "System.Collections.Generic.IList`1".
+ //
+ internal sealed partial class NamespaceTypeName : NamedTypeName
+ {
+ public NamespaceTypeName(string[] namespaceParts, string name)
+ {
+ Debug.Assert(namespaceParts != null);
+ Debug.Assert(name != null);
+
+ _name = name;
+ _namespaceParts = namespaceParts;
+ }
+
+ public sealed override string ToString()
+ {
+ string fullName = "";
+ for (int i = 0; i < _namespaceParts.Length; i++)
+ {
+ fullName += _namespaceParts[_namespaceParts.Length - i - 1];
+ fullName += ".";
+ }
+ fullName += _name;
+ return fullName;
+ }
+
+ private string _name;
+ private string[] _namespaceParts;
+ }
+
+ //
+ // A nested type. The Name is the simple name of the type (not including any portion of its declaring type name.)
+ //
+ internal sealed class NestedTypeName : NamedTypeName
+ {
+ public NestedTypeName(string name, NamedTypeName declaringType)
+ {
+ Name = name;
+ DeclaringType = declaringType;
+ }
+
+ public string Name { get; private set; }
+ public NamedTypeName DeclaringType { get; private set; }
+
+ public sealed override string ToString()
+ {
+ // Cecil's format uses '/' instead of '+' for nested types.
+ return DeclaringType + "/" + Name;
+ }
+ }
+
+ //
+ // Abstract base for array, byref and pointer type names.
+ //
+ internal abstract class HasElementTypeName : NonQualifiedTypeName
+ {
+ public HasElementTypeName(TypeName elementTypeName)
+ {
+ ElementTypeName = elementTypeName;
+ }
+
+ public TypeName ElementTypeName { get; }
+ }
+
+ //
+ // A single-dimensional zero-lower-bound array type name.
+ //
+ internal sealed class ArrayTypeName : HasElementTypeName
+ {
+ public ArrayTypeName(TypeName elementTypeName)
+ : base(elementTypeName)
+ {
+ }
+
+ public sealed override string ToString()
+ {
+ return ElementTypeName + "[]";
+ }
+ }
+
+ //
+ // A multidim array type name.
+ //
+ internal sealed class MultiDimArrayTypeName : HasElementTypeName
+ {
+ public MultiDimArrayTypeName(TypeName elementTypeName, int rank)
+ : base(elementTypeName)
+ {
+ _rank = rank;
+ }
+
+ public sealed override string ToString()
+ {
+ return ElementTypeName + "[" + (_rank == 1 ? "*" : new string(',', _rank - 1)) + "]";
+ }
+
+ private int _rank;
+ }
+
+ //
+ // A byref type.
+ //
+ internal sealed class ByRefTypeName : HasElementTypeName
+ {
+ public ByRefTypeName(TypeName elementTypeName)
+ : base(elementTypeName)
+ {
+ }
+
+ public sealed override string ToString()
+ {
+ return ElementTypeName + "&";
+ }
+ }
+
+ //
+ // A pointer type.
+ //
+ internal sealed class PointerTypeName : HasElementTypeName
+ {
+ public PointerTypeName(TypeName elementTypeName)
+ : base(elementTypeName)
+ {
+ }
+
+ public sealed override string ToString()
+ {
+ return ElementTypeName + "*";
+ }
+ }
+
+ //
+ // A constructed generic type.
+ //
+ internal sealed class ConstructedGenericTypeName : NonQualifiedTypeName
+ {
+ public ConstructedGenericTypeName(NamedTypeName genericType, IEnumerable<TypeName> genericArguments)
+ {
+ GenericType = genericType;
+ GenericArguments = genericArguments;
+ }
+
+ public NamedTypeName GenericType { get; }
+ public IEnumerable<TypeName> GenericArguments { get; }
+
+ public sealed override string ToString()
+ {
+ string s = GenericType.ToString();
+ s += "[";
+ string sep = "";
+ foreach (TypeName genericTypeArgument in GenericArguments)
+ {
+ s += sep;
+ sep = ",";
+ AssemblyQualifiedTypeName assemblyQualifiedTypeArgument = genericTypeArgument as AssemblyQualifiedTypeName;
+ if (assemblyQualifiedTypeArgument == null || assemblyQualifiedTypeArgument.AssemblyName == null)
+ s += genericTypeArgument.ToString();
+ else
+ s += "[" + genericTypeArgument.ToString() + "]";
+ }
+ s += "]";
+ return s;
+ }
+ }
+} \ No newline at end of file
diff --git a/external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeParser.cs b/external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeParser.cs
new file mode 100644
index 000000000..c06cf7667
--- /dev/null
+++ b/external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeParser.cs
@@ -0,0 +1,225 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+
+namespace System.Reflection.Runtime.TypeParsing
+{
+ //
+ // Parser for type names passed to GetType() apis.
+ //
+ public sealed class TypeParser
+ {
+ //
+ // Parses a typename. The typename may be optionally postpended with a "," followed by a legal assembly name.
+ //
+ public static TypeName ParseTypeName(string s)
+ {
+ try
+ {
+ return ParseAssemblyQualifiedTypeName(s);
+ }
+ catch (ArgumentException)
+ {
+ return null;
+ }
+ }
+
+ //
+ // Parses a typename. The typename may be optionally postpended with a "," followed by a legal assembly name.
+ //
+ private static TypeName ParseAssemblyQualifiedTypeName(String s)
+ {
+ if (string.IsNullOrEmpty(s))
+ return null;
+
+ // Desktop compat: a whitespace-only "typename" qualified by an assembly name throws an ArgumentException rather than
+ // a TypeLoadException.
+ int idx = 0;
+ while (idx < s.Length && Char.IsWhiteSpace(s[idx]))
+ {
+ idx++;
+ }
+ if (idx < s.Length && s[idx] == ',')
+ throw new ArgumentException();
+
+ try
+ {
+ TypeParser parser = new TypeParser(s);
+ NonQualifiedTypeName typeName = parser.ParseNonQualifiedTypeName();
+ TokenType token = parser._lexer.GetNextToken();
+ if (token == TokenType.End)
+ return typeName;
+ if (token == TokenType.Comma)
+ {
+ RuntimeAssemblyName assemblyName = parser._lexer.GetNextAssemblyName();
+ token = parser._lexer.Peek;
+ if (token != TokenType.End)
+ throw new ArgumentException();
+ return new AssemblyQualifiedTypeName(typeName, assemblyName);
+ }
+ throw new ArgumentException();
+ }
+ catch (TypeLexer.IllegalEscapeSequenceException)
+ {
+ // Emulates a CLR4.5 bug that causes any string that contains an illegal escape sequence to be parsed as the empty string.
+ return ParseAssemblyQualifiedTypeName(String.Empty);
+ }
+ }
+
+ private TypeParser(String s)
+ {
+ _lexer = new TypeLexer(s);
+ }
+
+
+ //
+ // Parses a type name without any assembly name qualification.
+ //
+ private NonQualifiedTypeName ParseNonQualifiedTypeName()
+ {
+ // Parse the named type or constructed generic type part first.
+ NonQualifiedTypeName typeName = ParseNamedOrConstructedGenericTypeName();
+
+ // Iterate through any "has-element" qualifiers ([], &, *).
+ for (;;)
+ {
+ TokenType token = _lexer.Peek;
+ if (token == TokenType.End)
+ break;
+ if (token == TokenType.Asterisk)
+ {
+ _lexer.Skip();
+ typeName = new PointerTypeName(typeName);
+ }
+ else if (token == TokenType.Ampersand)
+ {
+ _lexer.Skip();
+ typeName = new ByRefTypeName(typeName);
+ }
+ else if (token == TokenType.OpenSqBracket)
+ {
+ _lexer.Skip();
+ token = _lexer.GetNextToken();
+ if (token == TokenType.Asterisk)
+ {
+ typeName = new MultiDimArrayTypeName(typeName, 1);
+ token = _lexer.GetNextToken();
+ }
+ else
+ {
+ int rank = 1;
+ while (token == TokenType.Comma)
+ {
+ token = _lexer.GetNextToken();
+ rank++;
+ }
+ if (rank == 1)
+ typeName = new ArrayTypeName(typeName);
+ else
+ typeName = new MultiDimArrayTypeName(typeName, rank);
+ }
+ if (token != TokenType.CloseSqBracket)
+ throw new ArgumentException();
+ }
+ else
+ {
+ break;
+ }
+ }
+ return typeName;
+ }
+
+ //
+ // Foo or Foo+Inner or Foo[String] or Foo+Inner[String]
+ //
+ private NonQualifiedTypeName ParseNamedOrConstructedGenericTypeName()
+ {
+ NamedTypeName namedType = ParseNamedTypeName();
+ // Because "[" is used both for generic arguments and array indexes, we must peek two characters deep.
+ if (!(_lexer.Peek == TokenType.OpenSqBracket && (_lexer.PeekSecond == TokenType.Other || _lexer.PeekSecond == TokenType.OpenSqBracket)))
+ return namedType;
+ else
+ {
+ _lexer.Skip();
+ List<TypeName> genericTypeArguments = new List<TypeName>();
+ for (;;)
+ {
+ TypeName genericTypeArgument = ParseGenericTypeArgument();
+ genericTypeArguments.Add(genericTypeArgument);
+ TokenType token = _lexer.GetNextToken();
+ if (token == TokenType.CloseSqBracket)
+ break;
+ if (token != TokenType.Comma)
+ throw new ArgumentException();
+ }
+
+ return new ConstructedGenericTypeName(namedType, genericTypeArguments);
+ }
+ }
+
+ //
+ // Foo or Foo+Inner
+ //
+ private NamedTypeName ParseNamedTypeName()
+ {
+ NamedTypeName namedType = ParseNamespaceTypeName();
+ while (_lexer.Peek == TokenType.Plus)
+ {
+ _lexer.Skip();
+ String nestedTypeName = _lexer.GetNextIdentifier();
+ namedType = new NestedTypeName(nestedTypeName, namedType);
+ }
+ return namedType;
+ }
+
+ //
+ // Non-nested named type.
+ //
+ private NamespaceTypeName ParseNamespaceTypeName()
+ {
+ string fullName = _lexer.GetNextIdentifier();
+ string[] parts = fullName.Split('.');
+ int numNamespaceParts = parts.Length - 1;
+ string[] namespaceParts = new string[numNamespaceParts];
+ for (int i = 0; i < numNamespaceParts; i++)
+ namespaceParts[numNamespaceParts - i - 1] = parts[i];
+ string name = parts[numNamespaceParts];
+ return new NamespaceTypeName(namespaceParts, name);
+ }
+
+ //
+ // Parse a generic argument. In particular, generic arguments can take the special form [<typename>,<assemblyname>].
+ //
+ private TypeName ParseGenericTypeArgument()
+ {
+ TokenType token = _lexer.GetNextToken();
+ if (token == TokenType.Other)
+ {
+ NonQualifiedTypeName nonQualifiedTypeName = ParseNonQualifiedTypeName();
+ return nonQualifiedTypeName;
+ }
+ else if (token == TokenType.OpenSqBracket)
+ {
+ RuntimeAssemblyName assemblyName = null;
+ NonQualifiedTypeName typeName = ParseNonQualifiedTypeName();
+ token = _lexer.GetNextToken();
+ if (token == TokenType.Comma)
+ {
+ assemblyName = _lexer.GetNextEmbeddedAssemblyName();
+ token = _lexer.GetNextToken();
+ }
+ if (token != TokenType.CloseSqBracket)
+ throw new ArgumentException();
+ if (assemblyName == null)
+ return typeName;
+ else
+ return new AssemblyQualifiedTypeName(typeName, assemblyName);
+ }
+ else
+ throw new ArgumentException();
+ }
+
+ private readonly TypeLexer _lexer;
+ }
+} \ No newline at end of file